Migrating from Docker Compose
This guide is for teams running a compose.yaml (or docker-compose.yml) locally or on a single host who want to move to a managed Kubernetes deployment without rewriting their containers. Docker Compose and Convox describe the same building blocks, services, images, ports, environment, and dependencies, so most of the migration is a mechanical translation from one YAML file to another. Convox then runs those services on a Rack with rolling deploys, autoscaling, load balancing, and managed datastores that Compose does not provide.
Prerequisites
- A Convox Rack on the provider of your choice, and the
convoxCLI installed and logged in. - A
Dockerfilefor each service you build from source. Compose can build from aDockerfile, so if yourbuild:blocks already point at one you are set. If a service used a prebuiltimage:you can keep using it. See Dockerfile.
Concept Mapping
| Docker Compose | Convox equivalent |
|---|---|
services: |
services: in convox.yml |
build: (string or context/dockerfile) |
build: (path and manifest) |
image: |
image: |
command: |
command: |
ports: host:container mapping |
port: (the container port; the Rack assigns the public address) |
expose: / internal ports |
internal: true plus the service hostname |
environment: |
environment: |
env_file: |
convox env set (values live on the Release, not in the repo) |
named volumes: |
Volumes (volumes: / volumeOptions:, for persistence) |
volumes: host bind mount |
Not supported (see Gotchas) |
depends_on: and inter-service networking |
Service discovery by hostname; ordering via initContainer |
| database/cache service (postgres, redis, ...) | Resources |
deploy.replicas: |
scale.count |
restart: policy |
Automatic; Convox restarts failed Processes |
healthcheck: |
Health checks (health:, liveness:) |
convox.yml
Before: compose.yaml
services:
web:
build: .
command: bin/web
ports:
- "3000:3000"
environment:
- RAILS_ENV=production
- SECRET_KEY_BASE
depends_on:
- db
- cache
worker:
build: .
command: bin/worker
environment:
- RAILS_ENV=production
- SECRET_KEY_BASE
depends_on:
- db
- cache
db:
image: postgres:16
environment:
- POSTGRES_PASSWORD=secret
volumes:
- db-data:/var/lib/postgresql/data
cache:
image: redis:7
volumes:
db-data:
After: convox.yml
environment:
- RAILS_ENV=production
- SECRET_KEY_BASE
resources:
db:
type: postgres
cache:
type: redis
services:
web:
build: .
command: bin/web
port: 3000
resources:
- db
- cache
worker:
build: .
command: bin/worker
resources:
- db
- cache
The two postgres and redis containers from Compose become Convox Resources. You no longer declare the database image, password, or its data volume by hand, Convox provisions the datastore and injects its connection string. The web and worker services share the same build (the same Dockerfile) and differ only by command, exactly as they did in Compose.
Environment and Secrets
Compose reads environment: and env_file: at container start, often from values committed next to the source. In Convox, declare the variable names your services need in convox.yml and set their values on the App with convox env set. The values live on the Release, not in your repository.
environment:
- RAILS_ENV=production
- SECRET_KEY_BASE
A variable with a default (RAILS_ENV=production) is set in the manifest. A variable with no default (SECRET_KEY_BASE) must be supplied before you deploy:
$ convox env set SECRET_KEY_BASE=$(openssl rand -hex 64) -a myapp
Setting SECRET_KEY_BASE... OK
Release: RABCDEFGHI
Variables declared at the top level are available to every service; variables declared under a service apply only to that service. See Environment Variables.
Datastores
A postgres, mysql, mariadb, redis, or memcached service in your Compose file maps directly to a Convox Resource. Declare the resource, link it to the services that use it, and Convox runs the datastore and injects connection environment variables based on the resource name:
resources:
db:
type: postgres
services:
web:
build: .
port: 3000
resources:
- db
Linking the db resource injects DB_URL, DB_HOST, DB_PORT, DB_USER, DB_PASS, and DB_NAME into the web service. Point your app at DB_URL.
By default a Resource runs as a container inside the Rack, which is the closest match to a database container in Compose and is fine for development and staging. For production durability you can overlay the same resource onto a managed service (AWS RDS or ElastiCache, or a Convox Cloud database) without changing your application code:
resources:
db:
type: rds-postgres
options:
class: db.t3.large
storage: 100
encrypted: true
The injected DB_URL keeps the same format, so the switch is transparent to your code. See Resource for the full list of types, the managed overlays, and how to import an existing database. If your database already lives outside the cluster, set its connection string as an environment variable and skip the resource entirely.
Scheduled Jobs, Workers, and Cron
Compose does not have a native scheduler, so cron-style work is usually a separate long-running container or a host cron job. Convox splits these into two cleaner primitives:
- Long-running workers (queue consumers, background processors) are plain Services with a
commandand noport, exactly like theworkerservice above. - Scheduled jobs are Timers. A Timer runs a
commandagainst a named service on a cron schedule (UTC):
services:
jobs:
build: ./jobs
scale:
count: 0
timers:
nightly-cleanup:
command: bin/cleanup
schedule: "0 3 * * *"
service: jobs
The jobs service can be scaled to zero so it costs nothing when idle; the Timer spins up a Process on schedule. See Timer.
Deploy and Cutover
Once your convox.yml and Dockerfile are in place:
-
Set any environment variables that have no default in the manifest:
$ convox env set SECRET_KEY_BASE=... -a myapp -
Build and promote in one step:
$ convox deploy -a myappThis packages your source, builds each service image, creates a Release, and promotes it with a rolling deployment. See Deploying Changes.
-
Find the public address Convox assigned to each externally-routed service:
$ convox services -a myapp SERVICE DOMAIN PORTS web web.myapp.0a1b2c3d4e5f.convox.cloud 443:3000 -
Verify the new deployment over that hostname before sending real traffic. Run any one-off data migration with
convox runagainst the new Release.
Minimize downtime by cutting DNS over last. Deploy on Convox and validate it fully against the Convox-assigned hostname while your existing Compose host keeps serving production. Only after the Convox deployment is confirmed healthy do you point your custom domain at it (see Custom Domains). This keeps the old environment as a fallback until the cutover is complete.
Gotchas and What Is Different
- Host bind mounts do not carry over. Compose
volumes:entries like./src:/appmount a host directory into the container for live local development. Convox runs your code from the built image; there is no host filesystem to bind into. Convox Volumes exist for persistence (EFS, Azure Files) and scratch space (emptyDir), not for sharing source from a developer's machine. - Host networking and published host ports are gone. Compose
ports: "3000:3000"publishes a port on the host. On Convox you declare only the containerport:and the Rack provisions a load balancer and address for it. There is no host port mapping and nonetwork_mode: host. depends_onis not a startup gate by default. Convox does not order service startup the way Composedepends_onwithcondition: service_healthydoes. Services come up in parallel and reconnect once dependencies are ready. When a service genuinely must wait (for example, run migrations before booting), use aninitContainerto block until a dependency is healthy or a task has completed.- Inter-service calls use hostnames, not Compose service-name links. Compose lets one service reach another by its service name on a shared network. On Convox, reach another service by its service discovery hostname (mark backend-only services
internal: true). - Compose-only dev conveniences have no equivalent.
develop/watch,profiles,extends, andrestart:policies are local-development or single-host features. Convox restarts failed Processes automatically and uses Releases and the CLI for the deploy workflow instead. - Replica counts move to
scale. Translatedeploy.replicastoscale.count, and consider switching to autoscaling once you are running on a Rack.
See Also
- convox.yml for the full manifest reference
- Service for the complete list of service attributes
- Resource for databases, caches, and managed overlays
- Timer for scheduled jobs
- Environment Variables for managing configuration and secrets
- Service Discovery for inter-service networking
- Volumes for persistent and ephemeral storage
- Deploying Changes for the deploy and promote flow