How to Self-Host Convex with Dokploy or Docker Compose

Complete guide to self-hosting Convex backend on your own infrastructure using Dokploy or Docker Compose. Includes SQLite and PostgreSQL options.

How to Self-Host Convex with Dokploy or Docker Compose
Community & Learning

Join BitBuddies

Level up your DevOps skills with hands-on courses on CloudPanel and Dockploy. Join our community of developers and get expert workshops to accelerate your online journey.

Expert-led Courses
Live Workshops
Supportive Community
Practical Projects
Explore BitBuddies

Start your journey to DevOps mastery today! 🚀

If you’re building real-time applications with databases, you’ve probably heard of Convex. It’s a powerful backend-as-a-service platform that combines a real-time database with serverless functions. While Convex offers a generous free tier for cloud hosting, self-hosting gives you complete control over your data, infrastructure, and costs for production workloads.

In this comprehensive guide, I’ll show you how to self-host Convex on your own infrastructure using either Dokploy (the easiest method) or Docker Compose for more control. We’ll cover both simple SQLite deployments and production-ready PostgreSQL configurations.

What is Convex?

Convex is a modern backend platform that replaces traditional databases, API servers, and caching layers with a single, unified system. It provides real-time data synchronization, serverless functions, and built-in caching - all with a developer-friendly TypeScript API.

Key Features of Convex

  • Real-Time Database: Automatic data synchronization across all clients with reactive queries
  • TypeScript-First: End-to-end type safety from backend to frontend
  • Serverless Functions: Write queries, mutations, and actions in TypeScript without managing servers
  • Built-in Scheduling: Cron jobs and scheduled functions without external services
  • File Storage: Built-in file upload and storage with CDN distribution
  • Full-Text Search: Native search capabilities without Elasticsearch
  • Authentication: Flexible auth system supporting various providers
  • Atomic Transactions: ACID guarantees for data consistency
  • Time Travel: Query historical data and debug with time-travel queries
  • Vector Search: Built-in vector database for AI applications

Why Self-Host Convex?

Compared to Cloud-Hosted Convex:

  • Complete data ownership and privacy
  • No bandwidth or function execution limits
  • Custom infrastructure and scaling
  • Potential cost savings for high-traffic apps
  • Ability to run on-premises or in private networks

Cloud-Hosted Convex Free Tier:

  • 1GB storage
  • 1M function calls per month
  • Unlimited projects
  • Perfect for development and small apps

Self-hosting makes sense when you exceed these limits or need complete infrastructure control.

Prerequisites

Before you begin, make sure you have:

  • A VPS or Server: Minimum 2GB RAM and 2 CPU cores (4GB RAM recommended for PostgreSQL)
  • A Domain Name: For accessing your Convex backend (e.g., api.yourdomain.com)
  • Docker Installed: Docker and Docker Compose (Dokploy includes this)
  • Basic Command Line Knowledge: For running deployment commands

Hosting Recommendations

For development, a basic VPS with 2GB RAM works fine with SQLite. For production, use 4GB RAM with PostgreSQL hosted in the same region for optimal performance. Providers like Hetzner, DigitalOcean, or AWS work well.

Option 1: Deploy with Dokploy (Easiest Method)

Dokploy is an open-source Platform as a Service that makes deploying applications incredibly simple. Great news: Dokploy has a built-in Convex template! This means you can deploy Convex with just a few clicks. If you haven’t set up Dokploy yet, check out our Dokploy Installation Guide.

This is the absolute easiest way to deploy Convex. Dokploy has a built-in template that you can use as-is or customize with your own configuration!

Step 1: Install Dokploy (if not already installed)

curl -sSL https://dokploy.com/install.sh | sh

Access Dokploy at http://your-vps-ip:3000 and complete the setup.

Step 2: Deploy from Template

  1. Log in to Dokploy dashboard
  2. Click “Create Project” and name it (e.g., “Convex”)
  3. Click “Templates” in the left sidebar
  4. Search for “Convex” in the template gallery
  5. Click “Deploy” on the Convex template

Step 3: Customize the Configuration (Optional)

The template comes with a default configuration, but you can override it with your own settings:

  1. After deploying the template, go to the Docker Compose tab
  2. You’ll see the template’s default YAML - you can edit it if needed
  3. Go to the Environment tab to set your variables (see below)

Template Flexibility

The Dokploy template provides a great starting point, but you can completely customize it by editing the Docker Compose configuration and environment variables to match your specific needs!

services:
  backend:
    image: ghcr.io/get-convex/convex-backend:latest
    volumes:
      - data:/convex/data
    environment:
      - INSTANCE_NAME=${INSTANCE_NAME:-convex-self-hosted}
      - INSTANCE_SECRET=${INSTANCE_SECRET:-}
      - CONVEX_RELEASE_VERSION_DEV=${CONVEX_RELEASE_VERSION_DEV:-}
      - ACTIONS_USER_TIMEOUT_SECS=${ACTIONS_USER_TIMEOUT_SECS:-}
      - CONVEX_CLOUD_ORIGIN=${CONVEX_CLOUD_ORIGIN:-http://127.0.0.1:3210}
      - CONVEX_SITE_ORIGIN=${CONVEX_SITE_ORIGIN:-http://127.0.0.1:3211}
      - POSTGRES_URL=${POSTGRES_URL:-}
      - DISABLE_BEACON=${DISABLE_BEACON:-true}
      - REDACT_LOGS_TO_CLIENT=${REDACT_LOGS_TO_CLIENT:-}
      - RUST_LOG=${RUST_LOG:-info}
      - RUST_BACKTRACE=${RUST_BACKTRACE:-}
      - DO_NOT_REQUIRE_SSL=${DO_NOT_REQUIRE_SSL:-1}
    healthcheck:
      test: curl -f http://localhost:3210/version
      interval: 5s
      start_period: 5s

  dashboard:
    image: ghcr.io/get-convex/convex-dashboard:latest
    environment:
      - NEXT_PUBLIC_DEPLOYMENT_URL=${NEXT_PUBLIC_DEPLOYMENT_URL:-http://127.0.0.1:3210}
    depends_on:
      backend:
        condition: service_healthy

  postgres:
    image: postgres:17-alpine
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=${DB_PASSWORD}
      - POSTGRES_DB=convex_self_hosted
    volumes:
      - postgres-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5

volumes:
  data:
  postgres-data:

SQLite vs PostgreSQL

This configuration includes PostgreSQL for production use. If you want to use SQLite instead (simpler, good for development), leave POSTGRES_URL empty and Convex will automatically use SQLite. For production, set POSTGRES_URL to connect to PostgreSQL.

Step 4: Configure Environment Variables

Go to the Environment tab and add these variables:

Required:

  • INSTANCE_SECRET: Generate with openssl rand -hex 32

URLs - Choose one option:

Option A: Use Dokploy’s Free Traefik Domains (Quick start)

NEXT_PUBLIC_DEPLOYMENT_URL=http://shhosted-convex-cf33fb-91-98-95-196.traefik.me
CONVEX_CLOUD_ORIGIN=http://shhosted-convex-cf33fb-91-98-95-196.traefik.me
CONVEX_SITE_ORIGIN=http://shhosted-convex-59a34c-91-98-95-196.traefik.me

Option B: Use Your Own Domain (Production)

First, set up DNS A record: backend.convex.yourdomain.com → Your VPS IP

Then set:

CONVEX_CLOUD_ORIGIN=https://api.convex.yourdomain.com
CONVEX_SITE_ORIGIN=https://backend.convex.yourdomain.com
NEXT_PUBLIC_DEPLOYMENT_URL=https://api.convex.yourdomain.com

PostgreSQL (optional, leave empty to use SQLite):

DB_PASSWORD=your-strong-password
POSTGRES_URL=postgresql://postgres:your-strong-password@postgres:5432

Step 5: Configure Domain in Dokploy (if using custom domain)

  1. Go to the Domains tab
  2. Add your domain: backend.convex.yourdomain.com
  3. Dokploy’s Traefik will automatically handle SSL

Step 6: Deploy and Generate Admin Key

  1. Click “Deploy” and wait for services to start
  2. Once healthy, click “Terminal” to access the backend container
  3. Navigate and run:
cd convex
./generate_admin_key.sh
  1. Save the admin key securely

Option 2: Deploy with Docker Compose Only

If you prefer deploying without Dokploy or want more manual control, here’s how to use Docker Compose directly.

Step 1: Prepare Your Server

Update system and install Docker:

# Update packages
sudo apt update && sudo apt upgrade -y

# Install Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

# Install Docker Compose
sudo apt install docker-compose -y

Step 2: Create Project Directory

mkdir -p ~/convex-backend
cd ~/convex-backend

Step 3: Create Docker Compose File

For SQLite (Simple Setup):

nano docker-compose.yml

Paste:

services:
  backend:
    image: ghcr.io/get-convex/convex-backend:latest
    stop_grace_period: 10s
    stop_signal: SIGINT
    ports:
      - "3210:3210"
      - "3211:3211"
    volumes:
      - data:/convex/data
    environment:
      - INSTANCE_NAME=${INSTANCE_NAME:-convex-self-hosted}
      - INSTANCE_SECRET=${INSTANCE_SECRET}
      - CONVEX_CLOUD_ORIGIN=${CONVEX_CLOUD_ORIGIN:-http://127.0.0.1:3210}
      - CONVEX_SITE_ORIGIN=${CONVEX_SITE_ORIGIN:-http://127.0.0.1:3211}
      - RUST_LOG=${RUST_LOG:-info}
      - DISABLE_BEACON=${DISABLE_BEACON:-false}
    healthcheck:
      test: curl -f http://localhost:3210/version
      interval: 5s
      start_period: 10s

  dashboard:
    image: ghcr.io/get-convex/convex-dashboard:latest
    stop_grace_period: 10s
    stop_signal: SIGINT
    ports:
      - "6791:6791"
    environment:
      - NEXT_PUBLIC_DEPLOYMENT_URL=${NEXT_PUBLIC_DEPLOYMENT_URL:-http://127.0.0.1:3210}
    depends_on:
      backend:
        condition: service_healthy

volumes:
  data:

For PostgreSQL (Production Setup):

services:
  backend:
    image: ghcr.io/get-convex/convex-backend:latest
    stop_grace_period: 10s
    stop_signal: SIGINT
    ports:
      - "3210:3210"
      - "3211:3211"
    environment:
      - INSTANCE_NAME=${INSTANCE_NAME:-convex-self-hosted}
      - INSTANCE_SECRET=${INSTANCE_SECRET}
      - CONVEX_CLOUD_ORIGIN=${CONVEX_CLOUD_ORIGIN:-http://127.0.0.1:3210}
      - CONVEX_SITE_ORIGIN=${CONVEX_SITE_ORIGIN:-http://127.0.0.1:3211}
      - POSTGRES_URL=${POSTGRES_URL}
      - DO_NOT_REQUIRE_SSL=${DO_NOT_REQUIRE_SSL:-false}
      - RUST_LOG=${RUST_LOG:-info}
      - DOCUMENT_RETENTION_DELAY=${DOCUMENT_RETENTION_DELAY:-172800}
      - DISABLE_BEACON=${DISABLE_BEACON:-false}
    depends_on:
      postgres:
        condition: service_healthy
    healthcheck:
      test: curl -f http://localhost:3210/version
      interval: 5s
      start_period: 10s

  dashboard:
    image: ghcr.io/get-convex/convex-dashboard:latest
    stop_grace_period: 10s
    stop_signal: SIGINT
    ports:
      - "6791:6791"
    environment:
      - NEXT_PUBLIC_DEPLOYMENT_URL=${NEXT_PUBLIC_DEPLOYMENT_URL:-http://127.0.0.1:3210}
    depends_on:
      backend:
        condition: service_healthy

  postgres:
    image: postgres:17-alpine
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=${DB_PASSWORD}
      - POSTGRES_DB=convex_self_hosted
    volumes:
      - postgres-data:/var/lib/postgresql/data
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5

volumes:
  postgres-data:

Step 4: Create Environment File

nano .env

Add your configuration:

# Instance Configuration
INSTANCE_NAME=convex-self-hosted
INSTANCE_SECRET=<generate-with-openssl-rand-hex-32>

# Public URLs (update these for production)
CONVEX_CLOUD_ORIGIN=http://127.0.0.1:3210
CONVEX_SITE_ORIGIN=http://127.0.0.1:3211
NEXT_PUBLIC_DEPLOYMENT_URL=http://127.0.0.1:3210

# PostgreSQL Configuration (only if using PostgreSQL)
DB_PASSWORD=<your-secure-db-password>
POSTGRES_URL=postgresql://postgres:${DB_PASSWORD}@postgres:5432
DO_NOT_REQUIRE_SSL=true

# Optional: Logging
RUST_LOG=info

# Optional: Disable telemetry
DISABLE_BEACON=false

Generate secrets:

# Generate instance secret
openssl rand -hex 32

Step 5: Start Convex

# Start services
docker-compose up -d

# View logs
docker-compose logs -f

# Check status
docker-compose ps

Step 6: Set Up Reverse Proxy with Nginx

For production with custom domains, set up Nginx:

sudo apt install nginx certbot python3-certbot-nginx -y

Create Nginx configuration:

sudo nano /etc/nginx/sites-available/convex

Paste:

# Backend API
server {
    listen 80;
    server_name api.yourdomain.com;

    location / {
        proxy_pass http://localhost:3210;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

# Dashboard
server {
    listen 80;
    server_name dashboard.yourdomain.com;

    location / {
        proxy_pass http://localhost:6791;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Enable site and get SSL:

# Enable site
sudo ln -s /etc/nginx/sites-available/convex /etc/nginx/sites-enabled/

# Test configuration
sudo nginx -t

# Reload Nginx
sudo systemctl reload nginx

# Get SSL certificates
sudo certbot --nginx -d api.yourdomain.com -d dashboard.yourdomain.com

Update your .env file with production URLs:

CONVEX_CLOUD_ORIGIN=https://api.yourdomain.com
CONVEX_SITE_ORIGIN=https://backend.yourdomain.com
NEXT_PUBLIC_DEPLOYMENT_URL=https://api.yourdomain.com

Restart services:

docker-compose down
docker-compose up -d

Step 7: Generate Admin Key

# Access backend container
docker-compose exec backend /bin/sh

# Navigate to convex directory and generate admin key
cd convex
./generate_admin_key.sh

# Exit container
exit

Save the admin key securely - you’ll need it for your projects.

Using Convex in Your Projects

Now that your self-hosted Convex backend is running, here’s how to connect your applications to it.

Configure Your Project Environment

In your application’s .env.local file (don’t commit this to git), add these two environment variables:

CONVEX_SELF_HOSTED_URL=https://backend.convex.yourdomain.com
CONVEX_SELF_HOSTED_ADMIN_KEY=convex-self-hosted|015dfa7184876e556124a4ad005ffae7ace340d3d230a5c19106d94c1cdbb183bccf3dee06

Replace:

  • The URL with your actual backend URL (custom domain or traefik.me URL)
  • The admin key with the one you generated earlier

That’s it! The Convex CLI will automatically use your self-hosted backend when these variables are set.

Advanced Configuration

Using External PostgreSQL (Neon, Supabase, AWS RDS)

For managed PostgreSQL:

  1. Create a database named convex_self_hosted
  2. Get the connection string (without database name and query params)
  3. Update environment:
POSTGRES_URL=postgresql://user:[email protected]:5432
DO_NOT_REQUIRE_SSL=false

Example for Neon:

POSTGRES_URL=postgresql://user:[email protected]:5432

Same Region Required

Your Convex backend MUST be in the same region as your PostgreSQL database! Any latency between them directly impacts query performance.

Configuring S3 Storage

For production file storage, configure S3:

services:
  backend:
    environment:
      # ... other vars ...
      - AWS_REGION=us-east-1
      - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
      - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
      - S3_STORAGE_EXPORTS_BUCKET=convex-snapshot-exports
      - S3_STORAGE_SNAPSHOT_IMPORTS_BUCKET=convex-snapshot-imports
      - S3_STORAGE_MODULES_BUCKET=convex-modules
      - S3_STORAGE_FILES_BUCKET=convex-user-files
      - S3_STORAGE_SEARCH_BUCKET=convex-search-indexes

Create the buckets in AWS S3 or use S3-compatible storage like Cloudflare R2:

S3_ENDPOINT_URL=https://<account-id>.r2.cloudflarestorage.com
AWS_ACCESS_KEY_ID=<your-r2-access-key>
AWS_SECRET_ACCESS_KEY=<your-r2-secret-key>

Custom Domains for HTTP Actions

To serve HTTP actions from a custom domain:

  1. Set up DNS for api.yourdomain.com
  2. Configure in your environment:
CONVEX_SITE_ORIGIN=https://api.yourdomain.com
  1. In your frontend, override the environment variable during build:
CONVEX_SITE_URL=https://api.yourdomain.com

Migration Between Storage Providers

If switching from SQLite to PostgreSQL or changing S3 configuration:

# Export data from old backend
npx convex export --path backup.zip

# Deploy new backend with different storage
# ...

# Import data to new backend
npx convex import --replace-all backup.zip

Maintenance and Backups

Regular Backups

For Dokploy deployments, you can configure automated backups through Dokploy’s interface or follow our Dokploy Backups Guide.

For Docker Compose with PostgreSQL:

# Manual backup
docker-compose exec postgres pg_dump -U postgres convex_self_hosted > backup-$(date +%Y%m%d).sql

# Restore backup
cat backup-20241124.sql | docker-compose exec -T postgres psql -U postgres convex_self_hosted

Automated backup script (backup.sh):

#!/bin/bash
BACKUP_DIR="/backups/convex"
DATE=$(date +%Y%m%d-%H%M)
mkdir -p $BACKUP_DIR

docker-compose exec -T postgres pg_dump -U postgres convex_self_hosted | gzip > $BACKUP_DIR/convex-$DATE.sql.gz

# Keep only last 7 days
find $BACKUP_DIR -name "convex-*.sql.gz" -mtime +7 -delete

Make it executable and add to crontab:

chmod +x backup.sh
crontab -e
# Add: 0 2 * * * /path/to/backup.sh

Updating Convex

With Dokploy:

  1. Go to your service
  2. Click “Redeploy”
  3. Dokploy pulls latest image and restarts

With Docker Compose:

cd ~/convex-backend
docker-compose pull
docker-compose up -d

Version Pinning

For production, consider pinning to a specific version instead of :latest:

image: ghcr.io/get-convex/convex-backend:v0.1.0

Check releases for versions.

Security Best Practices

  • Secure Instance Secret: Use a strong, random INSTANCE_SECRET and never expose it
  • HTTPS Only: Always use SSL/TLS for production deployments
  • Strong Passwords: Use complex passwords for PostgreSQL and admin accounts
  • Firewall Configuration: Only expose necessary ports (80, 443)
  • Regular Updates: Keep Convex, Docker, and system packages updated
  • Backup Encryption: Encrypt database backups at rest
  • Environment Variables: Never commit .env files to version control
  • Admin Key Rotation: Regenerate admin keys periodically
  • Network Isolation: Use Docker networks to isolate services
  • Monitor Logs: Set up log monitoring for suspicious activity

Conclusion

Self-hosting Convex gives you complete control over your real-time backend infrastructure while maintaining the excellent developer experience Convex is known for. Whether you choose the simplicity of Dokploy or the flexibility of Docker Compose, you can have a production-ready Convex deployment in minutes.

The combination of Convex’s powerful features - real-time queries, serverless functions, file storage, and built-in search - with the control of self-hosting makes it an excellent choice for applications that need both developer productivity and infrastructure ownership.

For small to medium applications, the SQLite option provides excellent performance with minimal resource requirements. When you’re ready to scale, PostgreSQL gives you the reliability and performance needed for production workloads.

Next Steps

Learn More About Convex

Have questions about self-hosting Convex? Drop a comment below!

Frequently Asked Questions

SQLite is perfect for:

  • Development and testing
  • Small to medium applications (< 10k requests/day)
  • Single-server deployments
  • Quick prototypes

PostgreSQL is better for:

  • Production workloads with high traffic
  • Applications requiring high availability
  • Scenarios where you need database replication
  • When you need managed database services (Neon, Supabase, AWS RDS)

Start with SQLite and migrate to PostgreSQL when you need the extra reliability.

Yes! The process is straightforward:

  1. Export data from cloud: npx convex export --prod
  2. Set up self-hosted backend
  3. Import data: npx convex import --replace-all
  4. Update environment variables in your frontend
  5. Redeploy functions: npx convex deploy

Your application code doesn’t need to change - just update the backend URL.

Monthly costs (example):

  • VPS with 4GB RAM (Hetzner): $8/month
  • PostgreSQL on Neon: Free tier or $19/month for Pro
  • Domain: $1/month
  • S3 storage (optional): Pay per usage (~$1-5/month)

Total: $10-30/month depending on configuration

Compare to cloud Convex pricing for high-traffic apps - self-hosting can save significant costs at scale.

Absolutely! Many companies run self-hosted Convex in production. Make sure to:

  • Use PostgreSQL instead of SQLite
  • Set up automated backups
  • Configure proper monitoring
  • Use SSL/TLS
  • Run in a reliable hosting environment
  • Keep the backend updated

Follow the production deployment guidelines in this article for a reliable setup.

Self-hosted Convex supports all free-tier features:

  • Real-time queries and mutations
  • Serverless functions
  • File storage
  • Scheduled functions
  • Full-text search
  • Vector search
  • HTTP actions

The main difference is infrastructure management - you’re responsible for backups, scaling, and maintenance.

For vertical scaling:

  • Increase VPS resources (CPU/RAM)
  • Upgrade to a larger PostgreSQL instance
  • Use S3 for file storage to offload disk I/O

For horizontal scaling, you’ll need to:

  • Use a managed PostgreSQL with read replicas
  • Put Convex behind a load balancer
  • Configure multiple backend instances

Most applications won’t need horizontal scaling - vertical scaling handles substantial traffic.

Yes! Convex Auth works with self-hosted deployments. Follow the manual setup instructions in the Convex Auth documentation. The CLI’s automatic setup doesn’t support self-hosted yet, but manual configuration is straightforward.

With proper setup:

  • Docker will automatically restart crashed containers
  • PostgreSQL data persists in volumes
  • Recent operations may need to be retried by clients
  • Real-time subscriptions will reconnect automatically

For high availability:

  • Use a monitoring service (UptimeRobot, Pingdom)
  • Set up PostgreSQL replication
  • Consider running multiple backend instances
  • Implement regular automated backups

Related Posts