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.
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.
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.
Method A: Using Dokploy’s Built-in Template (Recommended)
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
- Log in to Dokploy dashboard
- Click “Create Project” and name it (e.g., “Convex”)
- Click “Templates” in the left sidebar
- Search for “Convex” in the template gallery
- 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:
- After deploying the template, go to the Docker Compose tab
- You’ll see the template’s default YAML - you can edit it if needed
- 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 withopenssl 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)
- Go to the Domains tab
- Add your domain:
backend.convex.yourdomain.com - Dokploy’s Traefik will automatically handle SSL
Step 6: Deploy and Generate Admin Key
- Click “Deploy” and wait for services to start
- Once healthy, click “Terminal” to access the backend container
- Navigate and run:
cd convex
./generate_admin_key.sh
- 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:
- Create a database named
convex_self_hosted - Get the connection string (without database name and query params)
- 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:
- Set up DNS for
api.yourdomain.com - Configure in your environment:
CONVEX_SITE_ORIGIN=https://api.yourdomain.com
- 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:
- Go to your service
- Click “Redeploy”
- 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.0Check releases for versions.
Security Best Practices
- Secure Instance Secret: Use a strong, random
INSTANCE_SECRETand 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
.envfiles 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
- Explore the Convex documentation for advanced features
- Set up automated backups with our Dokploy Backups Guide
- Join the Convex Discord #self-hosted channel for support
- Consider S3 storage for production file handling
- Monitor your deployment and optimize as needed
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:
- Export data from cloud:
npx convex export --prod - Set up self-hosted backend
- Import data:
npx convex import --replace-all - Update environment variables in your frontend
- 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
How To Update A Container With Docker Compose
How To Update A Container With Docker Compose is a tutorial that shows you how to use Docker Compose to update a container image and configuration without losing any data or settings.
Plausible.io - Google Analytics Lightweight Alternative
Plausible.io self-hosted Google analytics lightweight alternative
Multiple PostgreSQL Databases in ONE Service: THE Docker Compose WAY!
Master multiple PostgreSQL databases effortlessly! Discover how Docker Compose simplifies your setup. Don't miss out – transform your workflow NOW!