Skip to content

Deploying to Coolify

Coolify is a self-hosted PaaS that uses Docker Compose as its source of truth. It adds a Traefik reverse proxy, automatic HTTPS via Let’s Encrypt, and a web UI for managing deployments. All routing, env vars, and storage are configured in the compose file itself.

Official docs: coolify.io/docs/knowledge-base/docker/compose


A ready-to-use docker-compose.coolify.yml is included in the repo root:

# docker-compose.coolify.yml — Procella on Coolify
services:
procella:
build:
context: .
dockerfile: Dockerfile
# Or use a pre-built image:
# image: ghcr.io/your-org/procella:latest
environment:
# Non-sensitive config (set here or override in Coolify UI)
PROCELLA_LISTEN_ADDR: ":9090"
PROCELLA_AUTH_MODE: "descope" # or dev
PROCELLA_BLOB_BACKEND: "s3"
PROCELLA_BLOB_S3_REGION: "us-east-1"
# Sensitive values — set in Coolify UI under "Environment Variables"
# (leave as placeholders; Coolify will substitute)
PROCELLA_DATABASE_URL: "${PROCELLA_DATABASE_URL}"
PROCELLA_DEV_AUTH_TOKEN: "${PROCELLA_DEV_AUTH_TOKEN}"
PROCELLA_ENCRYPTION_KEY: "${PROCELLA_ENCRYPTION_KEY}"
PROCELLA_DESCOPE_PROJECT_ID: "${PROCELLA_DESCOPE_PROJECT_ID}"
PROCELLA_DESCOPE_MANAGEMENT_KEY: "${PROCELLA_DESCOPE_MANAGEMENT_KEY}"
PROCELLA_BLOB_S3_BUCKET: "${PROCELLA_BLOB_S3_BUCKET}"
PROCELLA_BLOB_S3_ENDPOINT: "${PROCELLA_BLOB_S3_ENDPOINT}"
AWS_ACCESS_KEY_ID: "${AWS_ACCESS_KEY_ID}"
AWS_SECRET_ACCESS_KEY: "${AWS_SECRET_ACCESS_KEY}"
# Tell Coolify to route procella.your-domain.com:443 → container:9090
labels:
- traefik.enable=true
- "traefik.http.routers.procella.rule=Host(`procella.your-domain.com`)"
- traefik.http.routers.procella.entryPoints=https
- traefik.http.routers.procella.tls.certresolver=letsencrypt
- traefik.http.services.procella.loadbalancer.server.port=9090
depends_on:
db:
condition: service_healthy
restart: unless-stopped
healthcheck:
test: ["CMD", "/procella", "--healthz"]
interval: 15s
timeout: 5s
retries: 3
start_period: 20s
db:
image: postgres:17-alpine
environment:
POSTGRES_DB: procella
POSTGRES_USER: procella
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
volumes:
- procella_pgdata:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U procella -d procella"]
interval: 10s
timeout: 5s
retries: 5
volumes:
procella_pgdata:
driver: local

In the Coolify dashboard:

  • Projects → New Resource → Docker Compose
  • Connect your Git repo (GitHub/GitLab/Gitea)
  • Set the compose file path to docker-compose.coolify.yml

Coolify will ask for a domain. Enter procella.your-domain.com. It appends :9090 automatically because the app container port is 9090.

Alternatively, use a Coolify magic variable in your compose:

environment:
# Coolify generates the FQDN automatically
COOLIFY_FQDN: "${SERVICE_FQDN_PROCELLA_9090}"

In the Coolify UI → your service → Environment Variables:

VariableValue
PROCELLA_DATABASE_URLpostgresql://procella:${POSTGRES_PASSWORD}@db:5432/procella
POSTGRES_PASSWORDopenssl rand -hex 24 output
PROCELLA_ENCRYPTION_KEYopenssl rand -hex 32 output
PROCELLA_DEV_AUTH_TOKENyour auth token
PROCELLA_DESCOPE_PROJECT_IDfrom Descope console
PROCELLA_DESCOPE_MANAGEMENT_KEYfrom Descope console

Click Deploy in the Coolify UI, or push to your linked branch for auto-deploy.


Section titled “External Postgres (Recommended for Production)”

Rather than bundling Postgres in the same compose stack, provision it as a separate Coolify resource (Database → PostgreSQL 17). This gives it its own volume, restart policy, and backup schedule.

Then in your procella service, reference it via the internal Docker network:

# Coolify renames services to <service>-<uuid> to avoid collisions.
# Use the "Connect to Predefined Network" option in Coolify UI,
# then reference the database by its Coolify-generated hostname.
environment:
PROCELLA_DATABASE_URL: "postgresql://procella:pass@postgres-abc123:5432/procella"

For a self-hosted setup, you can run MinIO alongside Procella:

services:
minio:
image: minio/minio:latest
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: "${MINIO_ROOT_USER}"
MINIO_ROOT_PASSWORD: "${MINIO_ROOT_PASSWORD}"
volumes:
- procella_minio:/data
labels:
- traefik.enable=true
- "traefik.http.routers.minio.rule=Host(`minio.your-domain.com`)"
- traefik.http.routers.minio.entryPoints=https
- traefik.http.routers.minio.tls.certresolver=letsencrypt
- traefik.http.services.minio.loadbalancer.server.port=9000
restart: unless-stopped

Then configure Procella to use it:

PROCELLA_BLOB_BACKEND: "s3"
PROCELLA_BLOB_S3_ENDPOINT: "http://minio:9000"
PROCELLA_BLOB_S3_BUCKET: "procella"
PROCELLA_BLOB_S3_REGION: "us-east-1"
AWS_ACCESS_KEY_ID: "${MINIO_ROOT_USER}"
AWS_SECRET_ACCESS_KEY: "${MINIO_ROOT_PASSWORD}"

  • Coolify uses your Dockerfile directly via build: { context: ., dockerfile: Dockerfile }. The bun build --compile binary runs without a Bun/Node runtime in the final distroless stage.
  • The distroless base image is compatible with Coolify’s Traefik sidecar network. Note: there is no shell inside the container, so docker exec debugging requires a debug image or sidecar.
  • Coolify adds coolify.managed=true and Traefik labels automatically if not present. Your explicit Traefik labels in the compose file take precedence.