Morphee Production Deployment with Coolify
Complete step-by-step guide to deploy Morphee v1.0 to production using Coolify
Status: ✅ Production-ready Last Updated: February 13, 2026 Estimated Time: 12-16 hours (over 2-3 days)
Table of Contents
- Overview
- Prerequisites
- Phase 1: Server Setup
- Phase 2: Domain Configuration
- Phase 3: Database & Auth
- Phase 5: Backend Deployment
- Phase 6: Verify Deployment
- Phase 7: Monitoring (Optional)
- Phase 8: Backups
- Phase 9: Testing
- Troubleshooting
- Cost Breakdown
Overview
What is Coolify?
Coolify is an open-source, self-hosted Platform-as-a-Service (PaaS) alternative to Heroku, Vercel, and Netlify. It provides:
- ✅ Docker Compose native support — deploy Morphee's multi-service architecture easily
- ✅ Built-in SSL via Let's Encrypt (automatic HTTPS)
- ✅ Built-in reverse proxy (Traefik) — routes traffic to your apps
- ✅ Environment variable management — secure secrets storage
- ✅ Zero-downtime deployments — rolling updates with health checks
- ✅ Git integration — auto-deploy on push to main
- ✅ Web UI — manage everything from a dashboard
Why Coolify for Morphee?
- Cost-effective: $17-42/mo vs $100+ on AWS
- Docker Compose ready: Morphee already has
docker-compose.yml - Easy migration: Move to Kubernetes later if needed
- Open source: No vendor lock-in
Prerequisites
Required
- ✅ Domain name —
morphee.app(or your domain)- Registered and accessible
- DNS managed via Cloudflare, DigitalOcean, or Route53
- ✅ LLM API key — Anthropic (recommended) or OpenAI
- Get key from: https://console.anthropic.com/settings/keys
- Or OpenAI: https://platform.openai.com/api-keys
- ✅ GitHub account — for repository access
- Morphee repo:
https://github.com/your-org/morphee-beta
- Morphee repo:
- ✅ Email account — for Coolify admin + Let's Encrypt
Recommended
- ⚡ Supabase account (free) — for managed PostgreSQL + Auth
- Sign up: https://supabase.com/dashboard
- Alternative: self-host PostgreSQL in Coolify
- ⚡ Server provider account — Hetzner (cheapest), DigitalOcean (easiest), or AWS
- ⚡ Payment method — credit card for server billing
Optional
- 🔧 AWS account — for S3 offsite backups
- 🔧 Google OAuth credentials — for Calendar/Gmail integrations
- 🔧 Firebase account — for mobile push notifications
- 🔧 Apple Developer account — for iOS push notifications
Phase 1: Server Setup (1-2 hours)
1.1 Choose Server Provider
Recommended: Hetzner Cloud (best price/performance)
| Specs | Price | Use Case |
|---|---|---|
| CPX21 — 3 vCPU, 4GB RAM, 80GB SSD | €8.19/mo (~$9/mo) | Small (100-500 users) |
| CPX31 — 4 vCPU, 8GB RAM, 160GB SSD | €16.09/mo (~$17/mo) | Medium (500-2000 users) |
Alternative: DigitalOcean
| Specs | Price | Use Case |
|---|---|---|
| Basic — 2 vCPU, 4GB RAM, 80GB SSD | $24/mo | Small |
| Basic — 2 vCPU, 8GB RAM, 160GB SSD | $48/mo | Medium |
1.2 Create Server
Hetzner Example:
- Go to https://console.hetzner.cloud
- Create new project: "Morphee Production"
- Add server:
- Location: Closest to your users (e.g., Ashburn, VA for US)
- Image: Ubuntu 22.04 LTS
- Type: CPX31 (4 vCPU, 8GB RAM) — recommended
- Networking: Enable IPv4 + IPv6
- SSH keys: Add your public key (
~/.ssh/id_rsa.pub) - Name:
morphee-prod
- Create server (takes ~60 seconds)
- Copy server IP address:
YOUR_SERVER_IP
DigitalOcean Example:
- Go to https://cloud.digitalocean.com
- Create Droplet:
- Image: Ubuntu 22.04 LTS
- Plan: Basic, $48/mo (2 vCPU, 8GB RAM, 160GB SSD)
- Datacenter: Closest to users
- Authentication: SSH keys (recommended)
- Hostname:
morphee-prod
- Create Droplet
- Copy IP address
1.3 Initial Server Configuration
SSH into server:
ssh root@YOUR_SERVER_IP
Update system:
apt update && apt upgrade -y
apt install -y curl wget git vim ufw fail2ban
Configure firewall:
# Allow SSH
ufw allow 22/tcp
# Allow HTTP/HTTPS (for Coolify)
ufw allow 80/tcp
ufw allow 443/tcp
# Allow Coolify dashboard (port 8000)
ufw allow 8000/tcp
# Enable firewall
ufw --force enable
# Verify
ufw status
Optional: Create non-root user
# Create user
adduser morphee
usermod -aG sudo morphee
# Add SSH key for new user
mkdir -p /home/morphee/.ssh
cp /root/.ssh/authorized_keys /home/morphee/.ssh/
chown -R morphee:morphee /home/morphee/.ssh
chmod 700 /home/morphee/.ssh
chmod 600 /home/morphee/.ssh/authorized_keys
# Test login (from local machine)
ssh morphee@YOUR_SERVER_IP
# Disable root SSH login (security best practice)
sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
systemctl restart ssh
1.4 Install Coolify
One-line installer:
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
This installs:
- Docker Engine (latest)
- Docker Compose (v2)
- Coolify server (runs on port 8000)
- Traefik reverse proxy (for routing + SSL)
- PostgreSQL (Coolify's database)
- Redis (Coolify's cache)
Installation takes ~5-10 minutes.
Expected output:
✅ Docker installed
✅ Docker Compose installed
✅ Coolify installed
✅ Traefik proxy started
✅ Coolify server started
🎉 Coolify is now running!
Access dashboard: http://YOUR_SERVER_IP:8000
Verify installation:
docker ps
You should see containers:
coolifycoolify-dbcoolify-rediscoolify-proxy(Traefik)
1.5 Access Coolify Dashboard
- Open browser:
http://YOUR_SERVER_IP:8000 - First-time setup wizard:
- Email: your-email@example.com (for Let's Encrypt SSL notifications)
- Username: admin (or your preferred username)
- Password: Create strong password (min 12 characters)
- Root Domain:
morphee.app(your domain)
- Click Complete Setup
- You'll be redirected to Coolify dashboard
Security: Restrict Dashboard Access
After setup, restrict Coolify dashboard to your IP only:
# On server
sudo ufw delete allow 8000/tcp
sudo ufw allow from YOUR_HOME_IP to any port 8000 proto tcp
# Verify
sudo ufw status
Access dashboard via SSH tunnel instead:
# From local machine
ssh -L 8000:localhost:8000 morphee@YOUR_SERVER_IP
# Then open: http://localhost:8000
Phase 2: Domain Configuration (30 minutes)
2.1 DNS Records
You need 3 subdomains:
api.morphee.app— Backend APIapp.morphee.app— Frontend web appmonitoring.morphee.app— Grafana (optional)
Option A: Individual A records
| Type | Name | Value | TTL |
|---|---|---|---|
| A | api | YOUR_SERVER_IP | 300 |
| A | app | YOUR_SERVER_IP | 300 |
| A | monitoring | YOUR_SERVER_IP | 300 |
| A | www | YOUR_SERVER_IP | 300 |
| A | @ (root) | YOUR_SERVER_IP | 300 |
Option B: Wildcard (simpler)
| Type | Name | Value | TTL |
|---|---|---|---|
| A | * | YOUR_SERVER_IP | 300 |
| A | @ | YOUR_SERVER_IP | 300 |
Cloudflare Example:
- Go to https://dash.cloudflare.com
- Select your domain (
morphee.app) - Go to DNS → Records
- Click Add record
- Add records above
- Click Save
Verify DNS propagation:
# Test from local machine
dig api.morphee.app +short
# Should return: YOUR_SERVER_IP
dig app.morphee.app +short
# Should return: YOUR_SERVER_IP
DNS propagation takes 5-60 minutes (usually ~5 min with Cloudflare).
2.2 SSL Certificates
Coolify uses Let's Encrypt for automatic SSL certificates.
Wildcard SSL setup (recommended):
- Go to Coolify → Settings → SSL
- Choose DNS Provider: Cloudflare (or your provider)
- Add API Token:
- Cloudflare: Go to My Profile → API Tokens → Create Token
- Use template: Edit zone DNS
- Permissions:
Zone.DNS.Editformorphee.app - Copy token
- Paste token in Coolify
- Click Request Wildcard Certificate
- Coolify will request
*.morphee.appcert via DNS-01 challenge - Wait ~2 minutes for certificate issuance
Verify SSL:
curl -I https://api.morphee.app
# Should return "HTTP/2 200" (after backend is deployed)
Phase 3: Database & Auth (Automatic)
With docker-compose.coolify.yml, PostgreSQL and GoTrue are self-hosted in the same stack. No external Supabase account needed.
Everything is automatic on first deploy:
- PostgreSQL 15 with pgvector starts with auto-generated password (
SERVICE_PASSWORD_POSTGRES) - Migrations run automatically via the
migrationsone-shot container (tracks applied migrations in_migrationstable, safe to re-run) - GoTrue connects to PostgreSQL internally, uses auto-generated JWT secret shared with backend
- No manual setup — no
psql, noCREATE EXTENSION, no migration scripts to run
SMTP Configuration (required for email confirmation)
GoTrue sends confirmation emails on signup. You need an SMTP provider:
| Provider | Free Tier | Setup |
|---|---|---|
| Resend | 3,000 emails/mo | Set SMTP_HOST=smtp.resend.com, SMTP_USER=resend, SMTP_PASS=re_... |
| Mailgun | 1,000 emails/mo | Set SMTP_HOST=smtp.mailgun.org, user + pass from Mailgun dashboard |
| SendGrid | 100 emails/day | Set SMTP_HOST=smtp.sendgrid.net, SMTP_USER=apikey, SMTP_PASS=SG... |
Set these in Coolify UI:
SMTP_HOST=smtp.resend.com
SMTP_PORT=587
SMTP_USER=resend
SMTP_PASS=re_your_api_key
SMTP_FROM=noreply@morphee.app
Quick start without SMTP: Set GOTRUE_MAILER_AUTOCONFIRM=true to skip email confirmation (not recommended for production).
SSO Providers (optional)
To enable Google/Apple/Microsoft login, set in Coolify UI:
# Google SSO
GOOGLE_SSO_ENABLED=true
GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-your-client-secret
GOTRUE_GOOGLE_REDIRECT_URI=http://supabase-auth:9999/callback
# Apple SSO
APPLE_SSO_ENABLED=true
APPLE_CLIENT_ID=app.morphee.mobile
APPLE_CLIENT_SECRET=your-apple-secret
# Microsoft / Azure AD SSO
AZURE_SSO_ENABLED=true
AZURE_CLIENT_ID=your-azure-client-id
AZURE_CLIENT_SECRET=your-azure-secret
Optional: External Supabase Database
If you prefer managed PostgreSQL (automatic backups, point-in-time recovery), use Supabase Cloud ($25/mo) instead:
- Create a project at https://supabase.com/dashboard
- In Coolify UI, override these variables:
DATABASE_URL→ Supabase connection stringSUPABASE_AUTH_URL→https://[PROJECT_ID].supabase.co/auth/v1SUPABASE_JWT_SECRET→ from Supabase Settings → API
- Remove or stop the
supabase-db,supabase-auth, andmigrationsservices in Coolify
Phase 5: Backend Deployment (2 hours)
5.1 Create Application in Coolify
-
Go to Coolify → Applications → New Application
-
Select deployment type:
- Choose Docker Compose
- Morphee has a Coolify-specific compose file
-
Connect Git repository:
- Source: GitHub
- Repository:
https://github.com/your-org/morphee-beta - Branch:
main - Compose file path:
docker-compose.coolify.yml(at root) - Click Connect
-
Assign domains to services:
- frontend:
https://app.morphee.app - backend:
https://api.morphee.app:8000 - Coolify's Traefik proxy routes traffic automatically — no port mapping needed
- frontend:
5.2 Configure Environment Variables
In Coolify → Application → Environment, set the required variables.
Auto-generated secrets (do NOT set manually):
Coolify auto-generates and persists these across redeployments:
| Variable | Purpose |
|---|---|
SERVICE_PASSWORD_POSTGRES | PostgreSQL password |
SERVICE_PASSWORD_REDIS | Redis authentication |
Required variables (set in Coolify UI):
# Secrets (generate ONCE, never change — see commands in compose file header)
JWT_SECRET=<base64-secret> # Generate: openssl rand -base64 32
ENCRYPTION_KEY=<fernet-key> # Generate: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
# URLs
VITE_API_URL=https://api.morphee.app
VITE_WS_URL=wss://api.morphee.app
CORS_ORIGINS=https://app.morphee.app,tauri://localhost,http://tauri.localhost,https://tauri.localhost
FRONTEND_URL=https://app.morphee.app
# LLM (at least one required for chat)
ANTHROPIC_API_KEY=sk-ant-api03-YOUR_KEY_HERE
Generate the Fernet encryption key (run once):
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
Optional variables:
# SMTP (strongly recommended — see Phase 3 for providers)
SMTP_HOST=smtp.resend.com
SMTP_PORT=587
SMTP_USER=resend
SMTP_PASS=re_your_api_key
SMTP_FROM=noreply@morphee.app
# Google OAuth (Calendar/Gmail integrations)
GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-your-client-secret
GOOGLE_REDIRECT_URI=https://api.morphee.app/api/oauth/google/callback
# Push Notifications
APNS_KEY_ID=your-apns-key-id
APNS_TEAM_ID=your-apple-team-id
APNS_BUNDLE_ID=app.morphee.mobile
FCM_PROJECT_ID=your-firebase-project-id
Important:
- Click the lock icon next to sensitive vars (API keys, encryption key) — encrypts them at rest
- Mark
VITE_*variables as Build Variables — they are baked into the JS bundle at build time, not runtime
5.3 Persistent Volumes
The docker-compose.coolify.yml uses Coolify's is_directory: true directive to auto-create host directories on first deploy. No manual directory creation is needed.
| Volume | Container Path | Purpose |
|---|---|---|
./data/memory | /data/memory | Git-backed memory repos (per group) |
./data/files | /data/files | Sandboxed file storage (per group) |
redis-data | /data | Redis AOF persistence |
These are bind mounts relative to Coolify's deployment directory. Coolify creates the host directories automatically.
5.4 Deploy Backend
- Click "Deploy" button in Coolify
- Monitor build logs:
- Coolify will:
- Clone repo
- Build Docker image (using
Dockerfile.backend) - Start Redis container
- Start backend container
- Run health check
- Coolify will:
- Wait for "Deployed" status (green checkmark)
- Check logs:
-
Coolify → Application → Logs
-
Look for:
INFO: Started server process
INFO: Uvicorn running on http://0.0.0.0:8000
INFO: Application startup complete
-
5.5 Verify Backend Deployment
Test health endpoint:
curl https://api.morphee.app/health
# Expected: {"status":"healthy","version":"2.0.0"}
Test API docs (if enabled):
curl https://api.morphee.app/docs
# Should return HTML with FastAPI Swagger UI
Test database connection:
# SSH into server
ssh morphee@YOUR_SERVER_IP
# Exec into backend container
docker exec -it morphee-backend bash
# Test database
python -c "
import asyncio
from db.client import get_db
async def check():
db = get_db()
await db.initialize()
health = await db.health_check()
print('Database health:', health)
asyncio.run(check())
"
# Exit container
exit
5.6 Troubleshooting Backend
Issue: Build fails
# Check build logs in Coolify UI
# Common issues:
# - Missing requirements in requirements.txt
# - Python 3.12 base image pull failure
# - Out of disk space
# Fix: SSH into server, check disk
df -h
# Clean up old Docker images if needed
docker system prune -a
Issue: Health check fails
# Check backend logs
docker logs morphee-backend -f
# Common issues:
# - DATABASE_URL not set or incorrect
# - Redis not reachable
# - Port 8000 already in use
# Verify environment variables
docker exec morphee-backend env | grep DATABASE_URL
Issue: 502 Bad Gateway
# Backend is not listening on port 8000
# Check backend logs for errors
docker logs morphee-backend --tail 50
# Verify backend is running
docker ps | grep backend
# Check if port 8000 is bound
docker exec morphee-backend netstat -tlnp | grep 8000
Phase 6: Verify Deployment (30 minutes)
With docker-compose.coolify.yml, frontend, backend, and Redis all deploy together as one stack. There is no separate frontend deployment step.
6.1 Deploy the Stack
- Click "Deploy" button in Coolify
- Monitor build logs — Coolify will:
- Clone repo
- Build frontend image (npm ci → Vite build → nginx)
- Build backend image (Python + dependencies)
- Pull Redis image
- Start all 3 services
- Run health checks
- Wait for "Deployed" status (green checkmark)
6.2 Verify Frontend
Test frontend loads:
curl https://app.morphee.app | grep -i morphee
Open in browser: https://app.morphee.app — you should see the login page.
Test API connection (browser console):
fetch('https://api.morphee.app/health')
.then(r => r.json())
.then(console.log)
// Expected: {status: "healthy", version: "2.0.0"}
6.3 Troubleshooting
Issue: Blank page
// Check browser console for errors
// Verify build args were applied
console.log(import.meta.env.VITE_API_URL)
// Should show: https://api.morphee.app
// If undefined: VITE_* vars need to be marked as Build Variables in Coolify
Issue: CORS errors
# Verify CORS_ORIGINS includes frontend domain
docker exec <backend-container> env | grep CORS_ORIGINS
# Should include: https://app.morphee.app
Issue: Assets 404
# Check nginx serves files
docker exec <frontend-container> ls -la /usr/share/nginx/html
Phase 7: Monitoring (Optional - 2 hours)
Option A: Grafana Cloud (Recommended for MVP)
Why Grafana Cloud?
- ✅ Free tier: 10k metrics, 50GB logs, 14-day retention
- ✅ Managed: No server setup required
- ✅ Professional dashboards: Pre-built Prometheus dashboards
- ✅ Alerts: Email/Slack notifications
Setup:
- Sign up: https://grafana.com/auth/sign-up
- Create stack: Choose closest region
- Add Prometheus data source:
- Copy Remote Write URL from Grafana Cloud
- Configure Prometheus to remote-write metrics
- Import dashboards:
- Node Exporter Full (ID: 1860)
- Docker Container Metrics (ID: 193)
Cost: $0/mo (free tier), $8/mo for 200k metrics (paid)
Option B: Self-Hosted Grafana + Prometheus
Deploy monitoring stack:
-
Upload compose file to server:
# From local machine
scp docker-compose.monitoring.yml morphee@YOUR_SERVER_IP:/opt/morphee/
scp prometheus.yml morphee@YOUR_SERVER_IP:/opt/morphee/ -
SSH into server and deploy:
ssh morphee@YOUR_SERVER_IP
cd /opt/morphee
# Set environment variables
export DATABASE_URL="postgresql://..."
export GRAFANA_PASSWORD="your-secure-password"
export GRAFANA_SECRET_KEY=$(openssl rand -base64 32)
# Start monitoring stack
docker compose -f docker-compose.monitoring.yml up -d
# Verify
docker ps | grep -E "prometheus|grafana|node-exporter" -
Access Grafana:
- Add DNS record:
monitoring.morphee.app→YOUR_SERVER_IP - Configure in Coolify (or add to Traefik config)
- Open:
https://monitoring.morphee.app - Login:
admin/YOUR_GRAFANA_PASSWORD
- Add DNS record:
-
Import dashboards:
- Go to Dashboards → Import
- Enter dashboard ID:
- 1860 — Node Exporter Full
- 193 — Docker Container Metrics
- 9628 — PostgreSQL Database
- Select Prometheus as data source
- Click Import
-
Verify metrics:
- Open "Node Exporter Full" dashboard
- You should see: CPU usage, RAM, disk, network
Phase 8: Backups (1 hour)
8.1 Database Backups
Option A: Supabase Automatic Backups (RECOMMENDED)
If using Supabase Database:
- ✅ Automatic daily backups included (7-day retention on Pro)
- ✅ Point-in-time recovery (restore to any minute within 7 days)
- ✅ Download backups via dashboard (Settings → Database → Backups)
Option B: Manual Backup Script
-
Upload backup script:
scp scripts/backup-morphee.sh morphee@YOUR_SERVER_IP:/root/ -
SSH into server and configure:
ssh morphee@YOUR_SERVER_IP
# Make executable
chmod +x /root/backup-morphee.sh
# Test run
DATABASE_URL="postgresql://..." /root/backup-morphee.sh
# Verify backup created
ls -lh /data/backups/morphee/ -
Schedule daily backups via cron:
# Edit crontab
crontab -e
# Add line (runs daily at 2 AM)
0 2 * * * DATABASE_URL="postgresql://..." /root/backup-morphee.sh >> /var/log/morphee-backup.log 2>&1 -
Optional: Upload to S3 for offsite storage:
# Install AWS CLI
apt install -y awscli
# Configure AWS credentials
aws configure
# Enter: Access Key ID, Secret Access Key, Region (us-east-1)
# Test S3 upload
DATABASE_URL="postgresql://..." \
S3_BUCKET="s3://your-morphee-backups" \
/root/backup-morphee.sh
8.2 Test Disaster Recovery
Simulate data loss and restore:
# SSH into server
ssh morphee@YOUR_SERVER_IP
# 1. Create test data
psql $DATABASE_URL -c "INSERT INTO conversations (id, title, user_id, group_id, space_id, created_at, updated_at) VALUES (gen_random_uuid(), 'Test Conversation', (SELECT id FROM users LIMIT 1), (SELECT id FROM groups LIMIT 1), (SELECT id FROM spaces LIMIT 1), NOW(), NOW());"
# 2. Backup
DATABASE_URL="postgresql://..." /root/backup-morphee.sh
# 3. Delete test data
psql $DATABASE_URL -c "DELETE FROM conversations WHERE title = 'Test Conversation';"
# 4. Restore from latest backup
LATEST_BACKUP=$(ls -t /data/backups/morphee/morphee_db_*.sql.gz | head -1)
gunzip -c $LATEST_BACKUP | psql $DATABASE_URL
# 5. Verify restoration
psql $DATABASE_URL -c "SELECT * FROM conversations WHERE title = 'Test Conversation';"
# Should return the restored row
Phase 9: Testing (1 hour)
9.1 Smoke Tests
Backend API:
# Health check
curl https://api.morphee.app/health
# Test signup
curl -X POST https://api.morphee.app/api/auth/signup \
-H "Content-Type: application/json" \
-d '{"email":"test@example.com","password":"Test1234!","name":"Test User"}'
# Test signin
curl -X POST https://api.morphee.app/api/auth/signin \
-H "Content-Type: application/json" \
-d '{"email":"test@example.com","password":"Test1234!"}'
# Save token from response
# Test authenticated endpoint
curl https://api.morphee.app/api/auth/me \
-H "Authorization: Bearer YOUR_TOKEN_HERE"
Frontend:
# Test HTML loads
curl https://app.morphee.app | grep -i morphee
# Test assets load
curl -I https://app.morphee.app/assets/index.js
# Should return 200 OK
WebSocket:
// In browser console (after login)
const ws = new WebSocket('wss://api.morphee.app/ws?token=YOUR_JWT_TOKEN');
ws.onopen = () => console.log('✅ WebSocket connected');
ws.onmessage = (e) => console.log('📨 Message:', e.data);
ws.onerror = (e) => console.error('❌ Error:', e);
9.2 End-to-End Test
Complete user flow:
- Open
https://app.morphee.appin browser - Click Sign Up
- Enter: email, password, name
- Click Create Account
- Should redirect to
/onboarding - Chat with AI: "I'm a parent with 2 kids"
- AI should create group + spaces
- Redirect to
/chat - Send message: "Hello Morphee!"
- AI should respond
- Check WebSocket events in DevTools → Network → WS
9.3 Load Test (Optional)
Install k6:
# macOS
brew install k6
# Linux
sudo gpg -k
sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
sudo apt-get update
sudo apt-get install k6
Run load test:
// load-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';
export let options = {
stages: [
{ duration: '2m', target: 100 }, // Ramp to 100 users
{ duration: '5m', target: 100 }, // Stay at 100
{ duration: '2m', target: 0 }, // Ramp down
],
thresholds: {
http_req_duration: ['p(95)<500'], // 95% < 500ms
http_req_failed: ['rate<0.01'], // <1% error rate
},
};
export default function () {
let response = http.get('https://api.morphee.app/health');
check(response, {
'status is 200': (r) => r.status === 200,
'response time < 500ms': (r) => r.timings.duration < 500,
});
sleep(1);
}
Run:
k6 run load-test.js
Expected results:
- ✅ 95th percentile < 500ms
- ✅ Error rate < 1%
- ✅ All checks pass
Troubleshooting
Common Issues
1. SSL certificate not provisioning
# Check Traefik logs
docker logs coolify-proxy -f
# Common issues:
# - DNS not propagated yet (wait 5-60 min)
# - Cloudflare DNS-only mode (should be "Proxied")
# - Rate limit (Let's Encrypt allows 5 certs/week per domain)
# Fix: Re-trigger certificate
# Coolify → Application → Domain → Re-issue Certificate
2. Backend can't connect to database
# Check DATABASE_URL format
# Correct: postgresql://user:pass@host:5432/dbname
# Incorrect: postgres://... (should be postgresql://)
# Test connection from backend container
docker exec morphee-backend bash -c "
python -c '
import asyncpg
import asyncio
async def test():
conn = await asyncpg.connect(\"$DATABASE_URL\")
print(\"Connected:\", await conn.fetchval(\"SELECT version()\"))
await conn.close()
asyncio.run(test())
'
"
3. Frontend can't reach backend (CORS)
# Check CORS_ORIGINS in backend
docker exec morphee-backend env | grep CORS_ORIGINS
# Should include: https://app.morphee.app
# Update in Coolify → Backend → Environment → CORS_ORIGINS
# Redeploy backend
4. WebSocket connection fails
// In browser console, check error
const ws = new WebSocket('wss://api.morphee.app/ws?token=YOUR_TOKEN');
ws.onerror = (e) => console.error(e);
// Common issues:
// - Invalid JWT token
// - Backend not listening on /ws
// - Traefik not upgrading connection
// Verify WebSocket endpoint
curl -I https://api.morphee.app/ws
// Should return "426 Upgrade Required"
5. Out of disk space
# Check disk usage
df -h
# Clean up Docker images/containers
docker system prune -a -f --volumes
# Remove old logs
journalctl --vacuum-time=7d
# Check large directories
du -sh /var/lib/docker
du -sh /data/coolify
Cost Breakdown
Option 1: Fully Self-Contained (Recommended)
Everything in docker-compose.coolify.yml — no external accounts.
| Service | Provider | Cost/mo |
|---|---|---|
| Server (8GB RAM) | Hetzner CPX31 | €16 (~$17) |
| Domain + SSL | Cloudflare | $0 (free) |
| SMTP | Resend | $0 (free tier: 3k emails/mo) |
| Total | ~$17/mo |
Pros: Cheapest, full control, data sovereignty, no vendor dependency Cons: Single server (no redundancy), manual backups
Option 2: Hybrid (Managed Database)
Self-hosted app + managed PostgreSQL for automatic backups.
| Service | Provider | Cost/mo |
|---|---|---|
| Server (4GB RAM) | Hetzner CPX21 | €8 (~$9) |
| Database + Auth | Supabase Pro | $25 |
| Domain + SSL | Cloudflare | $0 (free) |
| Total | ~$34/mo |
Pros: Automatic backups, point-in-time recovery Cons: Vendor dependency, higher cost
Option 3: Full Managed (Future)
| Service | Provider | Cost/mo |
|---|---|---|
| Database | AWS RDS | $30 |
| App Hosting | AWS ECS Fargate | $30 |
| Load Balancer | AWS ALB | $20 |
| Redis | ElastiCache | $15 |
| CDN | CloudFront | $5 |
| Total | ~$100/mo |
When to migrate: >10k users, multi-region, compliance needs
Next Steps
After successful deployment:
-
✅ Update documentation:
- Mark deployment.md as "✅ Deployed"
- Add production URLs to README.md
-
✅ Set up monitoring alerts:
- CPU > 80% for 5 min
- Disk > 90%
- HTTP error rate > 5%
-
✅ Enable CI/CD:
- GitHub Actions → Coolify webhook
- Auto-deploy on push to main
-
✅ User testing:
- Invite beta users
- Monitor errors in Sentry/Grafana
-
✅ Performance tuning:
- Add database indexes if slow
- Enable Redis caching
- Optimize Docker images
Support
Need help?
- Coolify Docs: https://coolify.io/docs
- Coolify Discord: https://coolify.io/discord
- Morphee Issues: https://github.com/your-org/morphee-beta/issues
Emergency contacts:
- On-call: (add phone number)
- Email: ops@morphee.app
Last updated: February 13, 2026 Deployment status: ✅ Production-ready Next review: After first production deployment