Overview

Dokploy is your current deployment platform - a Docker-based deployment tool with web UI, Traefik reverse proxy, and Let’s Encrypt SSL integration.
This is the production-tested method currently running RippleCore on your Hetzner CPX32 VPS.

Why Dokploy?

Simple Setup

One-line installer gets you running in minutes

Web UI

Manage deployments through intuitive dashboard

Automatic SSL

Free Let’s Encrypt certificates with auto-renewal

Git Integration

Deploy directly from GitHub/GitLab repositories

Prerequisites

  • Hetzner CPX32 (4 vCPU, 8GB RAM) or equivalent
  • Ubuntu 24.04 LTS (recommended) or 22.04
  • Root or sudo access
  • Public IPv4 address
  • Registered domain name
  • DNS access (Cloudflare, Namecheap, etc.)
  • A records pointing to VPS IP
  • SSH client
  • Git
  • Code editor

Installation

Step 1: Prepare VPS

SSH into your Hetzner VPS:
ssh root@46.224.2.100  # Replace with your IP
Update system packages:
apt update && apt upgrade -y
Install essential tools:
apt install -y curl wget git nano htop ufw fail2ban

Step 2: Configure Firewall

# Allow SSH, HTTP, HTTPS
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw allow 3000/tcp  # Dokploy dashboard (optional)

# Enable firewall
ufw enable

# Enable fail2ban (brute-force protection)
systemctl enable fail2ban
systemctl start fail2ban

Step 3: Install Docker

Dokploy requires Docker. Install using official script:
# Install Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

# Enable Docker service
systemctl enable docker
systemctl start docker

# Verify installation
docker --version
docker compose version

Step 4: Install Dokploy

One-line installer:
curl -sSL https://dokploy.com/install.sh | sh
This will:
  • Install Docker (if not already installed)
  • Install Docker Compose
  • Set up Traefik reverse proxy
  • Create Dokploy containers
  • Configure SSL with Let’s Encrypt
Installation takes ~5 minutes.

Step 5: Access Dashboard

Open browser and navigate to:
http://46.224.2.100:3000  # Replace with your IP
Create admin account:
  • Email: your-email@example.com
  • Password: Strong password (save to password manager)
Dokploy dashboard runs on port 3000. You can disable public access after setup by removing the UFW rule.

DNS Configuration

Add A Records

In your domain registrar, add these A records:
TypeNameValueTTL
A@46.224.2.100Auto
Awww46.224.2.100Auto
Aapp46.224.2.100Auto
Aapi46.224.2.100Auto
Adocs46.224.2.100Auto
Expected domains:
  • yourdomain.com → Marketing website
  • app.yourdomain.com → Main application
  • api.yourdomain.com → API services
  • docs.yourdomain.com → Documentation

Verify DNS Propagation

# Check DNS resolution
dig app.yourdomain.com

# Should return: 46.224.2.100
Use whatsmydns.net to check global DNS propagation. Can take 1-48 hours.

Deployment Workflow

Phase 1: Infrastructure Services

1. PostgreSQL 18

  1. Dokploy DashboardNew ServiceDatabasePostgreSQL
  2. Configuration:
    Name: ripplecore-postgres
    Version: 18-alpine
    Database: ripplecore
    Username: ripplecore
    Password: [Generate strong password]
    Port: 5432
    Volume: Enabled (persistent storage)
    
  3. Deploy and wait for “Running” status
Connection String:
postgresql://ripplecore:YOUR_PASSWORD@ripplecore-postgres:5432/ripplecore

2. Redis 7

  1. Dokploy DashboardNew ServiceDatabaseRedis
  2. Configuration:
    Name: ripplecore-redis
    Version: 7-alpine
    Password: [Generate strong password]
    Port: 6379
    Volume: Enabled (persistent storage)
    
  3. Deploy and wait for “Running” status
Connection String:
redis://:YOUR_PASSWORD@ripplecore-redis:6379

Phase 2: Database Migrations

Create Migration Job

  1. Dokploy DashboardNew ServiceApplication
  2. Configuration:
    Name: ripplecore-migrations
    Type: Job (run once)
    Repository: https://github.com/your-username/ripplecore-forge
    Branch: main
    Build Context: .
    Dockerfile: apps/migrations/Dockerfile
    
  3. Environment Variables:
    DATABASE_URL=postgresql://ripplecore:YOUR_PASSWORD@ripplecore-postgres:5432/ripplecore
    NODE_ENV=production
    
  4. Advanced Settings:
    Restart Policy: no (run once)
    CPU: 0.5
    Memory: 512MB
    
  5. Deploy and monitor logs
Expected output:
✅ Database connection successful
✅ Migrations completed successfully!
✅ Applied migrations: 13

Phase 3: Main Application

Generate Better Auth Secret

Run locally:
npx @better-auth/cli secret
Copy the generated secret (32+ characters).

Deploy App

  1. Dokploy DashboardNew ServiceApplication
  2. Git Source:
    Name: ripplecore-app
    Repository: https://github.com/your-username/ripplecore-forge
    Branch: main
    Build Context: .
    Dockerfile: apps/app/Dockerfile
    
  3. Build Arguments (in Build Args section):
    DATABASE_URL=postgresql://ripplecore:YOUR_PASSWORD@ripplecore-postgres:5432/ripplecore
    REDIS_URL=redis://:YOUR_PASSWORD@ripplecore-redis:6379
    BETTER_AUTH_SECRET=YOUR_GENERATED_SECRET
    NEXT_PUBLIC_APP_URL=https://app.yourdomain.com
    NEXT_PUBLIC_WEB_URL=https://yourdomain.com
    
  4. Environment Variables (runtime):
    # Database & Cache
    DATABASE_URL=postgresql://ripplecore:YOUR_PASSWORD@ripplecore-postgres:5432/ripplecore
    REDIS_URL=redis://:YOUR_PASSWORD@ripplecore-redis:6379
    
    # Authentication
    BETTER_AUTH_SECRET=YOUR_GENERATED_SECRET
    BETTER_AUTH_URL=https://app.yourdomain.com
    BETTER_AUTH_TRUST_HOST=true
    
    # URLs
    NEXT_PUBLIC_APP_URL=https://app.yourdomain.com
    NEXT_PUBLIC_WEB_URL=https://yourdomain.com
    
    # System
    NODE_ENV=production
    PORT=3000
    STRICT_HEALTH_CHECK=false
    
  5. Domain Configuration:
    Domain: app.yourdomain.com
    Enable SSL: Yes (Let's Encrypt)
    Force HTTPS: Yes
    
  6. Port Mapping:
    Container Port: 3000
    
  7. Resource Limits:
    CPU: 1.0
    Memory: 1GB
    
  8. Health Check:
    Path: /api/health
    Interval: 30s
    Timeout: 10s
    Retries: 3
    
  9. Deploy and monitor build logs

Phase 4: API Service

Follow same pattern as App, with these differences:
Name: ripplecore-api
Dockerfile: apps/api/Dockerfile
Domain: api.yourdomain.com
Port: 3002

Phase 5: Marketing Website

Name: ripplecore-web
Dockerfile: apps/web/Dockerfile
Domain: yourdomain.com, www.yourdomain.com
Port: 3001

Environment Variables:
  - NEXT_PUBLIC_APP_URL=https://app.yourdomain.com
  - NEXT_PUBLIC_WEB_URL=https://yourdomain.com
  - NODE_ENV=production
  - PORT=3001

Note: No DATABASE_URL or REDIS_URL needed (static site)

Phase 6: Documentation (Optional)

Name: ripplecore-docs
Dockerfile: apps/docs/Dockerfile
Domain: docs.yourdomain.com
Port: 3004

Environment Variables:
  - NODE_ENV=production
  - PORT=3004

Verification

Test Health Endpoints

# App
curl https://app.yourdomain.com/api/health

# API
curl https://api.yourdomain.com/api/health

# Web
curl https://yourdomain.com/api/health
Expected response:
{
  "status": "ok",
  "timestamp": "2025-01-11T12:00:00.000Z",
  "service": "ripplecore-app",
  "checks": {
    "database": { "status": "ok" },
    "redis": { "status": "ok" }
  }
}

Test SSL Certificates

# Check SSL
curl -I https://app.yourdomain.com | grep "HTTP/2 200"

Test Application

  1. Navigate to https://app.yourdomain.com
  2. Sign up for account
  3. Verify email functionality
  4. Test evidence logging (kindness, volunteer, etc.)

Monitoring

Dokploy Built-in Monitoring

Access via dashboard:
  • CPU usage per container
  • Memory usage per container
  • Request rate
  • Container logs

Log Viewing

# View app logs
docker logs ripplecore-app --tail 100 --follow

# View PostgreSQL logs
docker logs ripplecore-postgres --tail 50

# View Redis logs
docker logs ripplecore-redis --tail 50

Set Up Alerts

In Dokploy dashboard:
  1. SettingsNotifications
  2. Add email or webhook for:
    • Health check failures
    • High CPU/Memory usage
    • Deployment failures

Updating Deployments

Code Updates

# 1. Push changes to Git
git add .
git commit -m "feat: add new feature"
git push origin main

# 2. Trigger redeployment in Dokploy
# Click "Redeploy" in dashboard for affected app

# 3. Monitor deployment logs
# 4. Verify health checks pass

Database Schema Updates

# 1. Generate migration locally
pnpm db:generate

# 2. Commit migration files
git add packages/database/migrations/
git commit -m "feat: add new table"
git push

# 3. Redeploy migration job in Dokploy
# 4. Restart app services

Environment Variable Updates

  1. Dokploy DashboardApps[app-name]Environment
  2. Edit variables
  3. Restart container
  4. Verify with health check

Troubleshooting

Issue: “Cannot find module ‘@repo/…’”Fix:
  • Verify build context is . (monorepo root)
  • Check pnpm-workspace.yaml is copied in Dockerfile
  • Ensure COPY command includes all packages
Issue: 503 health check responsesFix:
# Check PostgreSQL is running
docker ps | grep postgres

# Test connection
docker exec -it ripplecore-app sh
apk add postgresql-client
psql $DATABASE_URL -c "SELECT 1"
Verify connection string format:
postgresql://user:password@host:port/database
Issue: Let’s Encrypt SSL failingFix:
  • Verify DNS points to correct IP: dig app.yourdomain.com
  • Check firewall allows ports 80/443: ufw status
  • Wait for DNS propagation (up to 48 hours)
  • Check Dokploy logs for Let’s Encrypt errors
Issue: App container in restart loopFix:
# Check logs
docker logs ripplecore-app

# Common causes:
# 1. Out of memory → increase limit
# 2. Missing environment variables
# 3. Health check failing immediately
# 4. Application crash on startup

Resource Optimization

Current Allocation (Hetzner CPX32)

Total: 4 vCPU, 8 GB RAM

PostgreSQL: 0.5 vCPU, 1 GB RAM
Redis: 0.25 vCPU, 512 MB RAM
App: 1.0 vCPU, 1 GB RAM
API: 1.0 vCPU, 1 GB RAM
Web: 0.5 vCPU, 512 MB RAM
Docs: 0.25 vCPU, 256 MB RAM
System: 0.5 vCPU, 3.5 GB RAM

Total Used: 4 vCPU, 7.75 GB RAM (95% utilization)

Optimization Tips

  1. Enable caching - Redis cache already configured
  2. Use health checks - Automatic recovery from failures
  3. Monitor metrics - Watch for high CPU/memory usage
  4. Scale resources - Upgrade to CPX42 if needed (8 vCPU, 16GB RAM)

Pros & Cons

Advantages

  • Simple web UI
  • Automatic SSL
  • Git integration
  • Quick deployment
  • Production-tested

Limitations

  • Basic monitoring
  • Limited rollback
  • Fewer integrations
  • Single-server focus

Migration Path

From Dokploy to Coolify

If you need more features, see Coolify deployment guide. Migration steps:
  1. Set up Coolify on new VPS
  2. Deploy RippleCore to Coolify
  3. Test thoroughly
  4. Update DNS to point to new VPS
  5. Monitor for 24 hours
  6. Decommission Dokploy VPS
Estimated time: 2-3 hours

From Dokploy to Docker Compose

For maximum control, see Docker Compose guide. Migration steps:
  1. Export Dokploy configurations
  2. Create docker-compose.yml
  3. Set up Traefik manually
  4. Configure Let’s Encrypt
  5. Deploy and test
Estimated time: 4-6 hours

Next Steps

Monitoring Setup

Configure alerts and log aggregation

Backup Strategy

Set up database and volume backups

Explore Alternatives

Compare other deployment methods

Performance Tuning

Optimize resource allocation
Production Deployment Complete! Your RippleCore instance is now running with Dokploy on Hetzner VPS.