Skip to content

Instantly share code, notes, and snippets.

@hahagu
Created March 7, 2026 00:22
Show Gist options
  • Select an option

  • Save hahagu/d501a86381f5edca70a76ae608f89781 to your computer and use it in GitHub Desktop.

Select an option

Save hahagu/d501a86381f5edca70a76ae608f89781 to your computer and use it in GitHub Desktop.
Self Hosted Convex Docker Compose for Coolify
# Convex Self-Hosted — Coolify Docker Compose
# Deploy as a "Docker Compose" resource in Coolify.
#
# After deployment:
# 1. docker compose exec backend ./generate_admin_key.sh
# 2. Set CONVEX_SELF_HOSTED_ADMIN_KEY in your app's .env with the generated key
# 3. Set CONVEX_SELF_HOSTED_URL to the backend URL (SERVICE_URL_BACKEND_3210)
#
# Configuration:
# Set INSTANCE_NAME in Coolify UI to your project name (default: "convex").
# This value is used as both the Convex instance name and the PostgreSQL database name.
#
# Proxy routes (all auto-generated by Coolify):
# SERVICE_FQDN_BACKEND_3210 → backend:3210 (Convex API)
# SERVICE_FQDN_SITE → site:80 (nginx → backend:3211, Convex HTTP actions)
# SERVICE_FQDN_DASHBOARD_6791 → dashboard:6791 (Convex Dashboard)
# MINIO_SERVER_URL → minio:9000 (S3 API — Coolify special-case)
# MINIO_BROWSER_REDIRECT_URL → minio:9001 (MinIO Console — Coolify special-case)
#
# Coolify magic variables:
# - BARE declarations (e.g. `- SERVICE_FQDN_X_3000`) trigger FQDN generation
# AND Traefik label creation. The identifier MUST match the service name.
# - Declaring SERVICE_FQDN_X also auto-generates SERVICE_URL_X (and port variants).
# - VALUE references (e.g. `VAR=${SERVICE_URL_X}`) only consume an
# already-generated value — they do NOT trigger generation or Traefik labels.
# - Variables are stored at the stack level and shared across all services.
services:
# ──────────────────────────────────────────────
# PostgreSQL
# ──────────────────────────────────────────────
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
- POSTGRES_USER=${SERVICE_USER_POSTGRES:-convex}
- POSTGRES_PASSWORD=${SERVICE_PASSWORD_POSTGRES}
- POSTGRES_DB=${INSTANCE_NAME:-convex}
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER}"]
start_period: 20s
interval: 30s
retries: 5
timeout: 5s
# ──────────────────────────────────────────────
# MinIO (S3-compatible object storage)
# ──────────────────────────────────────────────
# NOTE: Official MinIO Docker images were discontinued Oct 2025.
# Using community-maintained builds from coollabsio/minio.
#
# Coolify has special-case FQDN handling for MINIO_SERVER_URL and
# MINIO_BROWSER_REDIRECT_URL — it auto-generates two separate FQDNs
# and creates Traefik labels for both ports (9000 + 9001).
minio:
image: ghcr.io/coollabsio/minio:latest
restart: unless-stopped
command: server /data --console-address ":9001"
environment:
- MINIO_ROOT_USER=$SERVICE_USER_MINIO
- MINIO_ROOT_PASSWORD=$SERVICE_PASSWORD_MINIO
- MINIO_SERVER_URL=${MINIO_SERVER_URL}
- MINIO_BROWSER_REDIRECT_URL=${MINIO_BROWSER_REDIRECT_URL}
volumes:
- minio-data:/data
healthcheck:
test: ["CMD", "mc", "ready", "local"]
interval: 5s
timeout: 20s
retries: 10
# ──────────────────────────────────────────────
# MinIO init (creates Convex buckets on first run)
# ──────────────────────────────────────────────
minio-init:
image: minio/mc:latest
restart: "no"
exclude_from_hc: true
depends_on:
minio:
condition: service_healthy
environment:
- MINIO_USER=$SERVICE_USER_MINIO
- MINIO_PASS=$SERVICE_PASSWORD_MINIO
entrypoint: >
/bin/sh -c "
mc alias set convex http://minio:9000 $${MINIO_USER} $${MINIO_PASS};
mc mb convex/convex-snapshot-exports --ignore-existing;
mc mb convex/convex-snapshot-imports --ignore-existing;
mc mb convex/convex-modules --ignore-existing;
mc mb convex/convex-user-files --ignore-existing;
mc mb convex/convex-search-indexes --ignore-existing;
echo 'All Convex buckets ready';
"
# ──────────────────────────────────────────────
# Convex Backend
# ──────────────────────────────────────────────
backend:
image: ghcr.io/get-convex/convex-backend:latest
restart: unless-stopped
stop_grace_period: 10s
stop_signal: SIGINT
environment:
# --- Proxy route: identifier matches service name → Coolify generates FQDN + Traefik ---
- SERVICE_FQDN_BACKEND_3210
# --- Core ---
- INSTANCE_NAME=${INSTANCE_NAME:-convex}
- INSTANCE_SECRET=${SERVICE_HEX_INSTANCE}
# --- Public URLs (value refs — consume auto-generated URLs) ---
- CONVEX_CLOUD_ORIGIN=${SERVICE_URL_BACKEND}
- CONVEX_SITE_ORIGIN=${SERVICE_URL_SITE}
# --- Database ---
- POSTGRES_URL=postgresql://${SERVICE_USER_POSTGRES:-convex}:${SERVICE_PASSWORD_POSTGRES}@postgres:5432
- DO_NOT_REQUIRE_SSL=true
# --- S3 / MinIO ---
- AWS_ACCESS_KEY_ID=$SERVICE_USER_MINIO
- AWS_SECRET_ACCESS_KEY=$SERVICE_PASSWORD_MINIO
- AWS_REGION=${AWS_REGION:-us-east-1}
- S3_ENDPOINT_URL=http://minio:9000
- AWS_S3_FORCE_PATH_STYLE=true
- AWS_S3_DISABLE_CHECKSUMS=true
- AWS_S3_DISABLE_SSE=true
- 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
# --- Performance tuning ---
- APPLICATION_MAX_CONCURRENT_MUTATIONS=${APPLICATION_MAX_CONCURRENT_MUTATIONS:-16}
- APPLICATION_MAX_CONCURRENT_QUERIES=${APPLICATION_MAX_CONCURRENT_QUERIES:-16}
- APPLICATION_MAX_CONCURRENT_NODE_ACTIONS=${APPLICATION_MAX_CONCURRENT_NODE_ACTIONS:-16}
- APPLICATION_MAX_CONCURRENT_V8_ACTIONS=${APPLICATION_MAX_CONCURRENT_V8_ACTIONS:-16}
- DOCUMENT_RETENTION_DELAY=${DOCUMENT_RETENTION_DELAY:-172800}
# --- Logging ---
- RUST_LOG=${RUST_LOG:-info}
- DISABLE_BEACON=${DISABLE_BEACON:-true}
- DISABLE_METRICS_ENDPOINT=${DISABLE_METRICS_ENDPOINT:-true}
volumes:
- backend-data:/convex/data
depends_on:
postgres:
condition: service_healthy
minio:
condition: service_healthy
minio-init:
condition: service_completed_successfully
healthcheck:
test: curl -f http://localhost:3210/version
interval: 5s
start_period: 10s
# ──────────────────────────────────────────────
# Site proxy (nginx → backend:3211)
# ──────────────────────────────────────────────
# Coolify requires the magic variable identifier to match the service name.
# Since backend already uses SERVICE_FQDN_BACKEND for port 3210, the site
# proxy (port 3211) runs as a separate nginx service named "site".
site:
image: nginx:alpine
restart: unless-stopped
environment:
- SERVICE_FQDN_SITE
entrypoint: ["/bin/sh", "-c"]
command:
- |
cat > /etc/nginx/conf.d/default.conf <<'CONF'
server {
listen 80;
server_name _;
location /health {
access_log off;
default_type text/plain;
return 200 'ok';
}
location / {
proxy_pass http://backend:3211;
proxy_http_version 1.1;
proxy_set_header Upgrade $$http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $$host;
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;
}
}
CONF
exec nginx -g 'daemon off;'
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://site/health || exit 1"]
interval: 10s
timeout: 5s
retries: 3
depends_on:
backend:
condition: service_healthy
# ──────────────────────────────────────────────
# Convex Dashboard
# ──────────────────────────────────────────────
dashboard:
image: ghcr.io/get-convex/convex-dashboard:latest
restart: unless-stopped
stop_grace_period: 10s
stop_signal: SIGINT
environment:
# Proxy route: identifier matches service name → Coolify generates FQDN + Traefik
- SERVICE_FQDN_DASHBOARD_6791
# Points dashboard to the backend API (cross-service value ref)
- NEXT_PUBLIC_DEPLOYMENT_URL=${SERVICE_URL_BACKEND}
healthcheck:
test: curl -f http://dashboard:6791
interval: 10s
timeout: 5s
retries: 3
depends_on:
backend:
condition: service_healthy
volumes:
postgres-data:
minio-data:
backend-data:
@hahagu
Copy link
Author

hahagu commented Mar 7, 2026

Self Hosted Convex Docker Compose for Coolify

This is a docker compose for coolify, that allows you to self host a convex database on your own coolify instance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment