H
Hostess

Migrate from Docker Compose

Convert your docker-compose.yml to hostess.yml and deploy to production.

If you're running your application with Docker Compose locally, migrating to Hostess is straightforward. This guide walks through converting a multi-service docker-compose.yml into a hostess.yml and deploying it to production.

What You'll Migrate

We'll convert a typical full-stack application with:

  • Next.js frontend
  • FastAPI backend
  • PostgreSQL database
  • Redis cache

The Starting Point

Here's a typical docker-compose.yml for this stack:

docker-compose.yml
version: "3.8"

services:
  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    environment:
      - NEXT_PUBLIC_API_URL=http://localhost:8000
    depends_on:
      - backend

  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgresql://postgres:mysecretpassword@db:5432/myapp
      - REDIS_URL=redis://redis:6379
      - JWT_SECRET=super-secret-key-change-me
    depends_on:
      - db
      - redis

  db:
    image: postgres:16
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=mysecretpassword
      - POSTGRES_DB=myapp
    volumes:
      - postgres_data:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data

volumes:
  postgres_data:
  redis_data:

Run hostess init

From your project directory (where docker-compose.yml lives), run:

Terminal
hostess init

Hostess detects docker-compose.yml, docker-compose.yaml, compose.yml, or compose.yaml in the current directory and generates a hostess.yml:

Created hostess.yml from docker-compose.yml (4 services)

Next steps:
  1. Review hostess.yml and adjust as needed
  2. Run: hostess deploy

Review the generated hostess.yml

Open the generated hostess.yml. For the Compose file above, in a directory named my-app, hostess init generates this shape:

hostess.yml
version: "1.0"
name: my-app

services:
  db:
    type: postgres
    resources: small

  redis:
    type: redis
    resources: small

  backend:
    type: fastapi
    build:
      source: ./backend
    ports:
      - 8000
    env:
      DATABASE_URL: ${db.url}
      JWT_SECRET: super-secret-key-change-me
      REDIS_URL: ${redis.url}
    depends_on:
      - db
      - redis

  frontend:
    type: nextjs
    build:
      source: ./frontend
    ports:
      - 3000
    env:
      NEXT_PUBLIC_API_URL: "http://localhost:8000"
    depends_on:
      - backend

This is a starting point. Before deploying, make the production-specific choices explicit:

hostess.yml
version: "1.0"
name: my-app

services:
  db:
    type: postgres
    resources: medium
    retention: permanent
    backups: daily

  redis:
    type: redis
    resources: small
    retention: permanent

  backend:
    type: fastapi
    build:
      source: ./backend
    env:
      DATABASE_URL: ${db.url}
      JWT_SECRET: ${secret:JWT_SECRET}
      REDIS_URL: ${redis.url}
    depends_on:
      - db
      - redis

  frontend:
    type: nextjs
    build:
      source: ./frontend
    env:
      NEXT_PUBLIC_API_URL: ${backend.external_url}
    depends_on:
      - backend

What Changed (and Why)

Here's a side-by-side comparison of each major difference:

Ports become service-level ports. Hostess uses the service type defaults for common frameworks. Next.js defaults to 3000, and FastAPI defaults to 8000. You can keep explicit ports: when a service listens on a different port.

frontend:
  ports:
    - "3000:3000"
backend:
  ports:
    - "8000:8000"
frontend:
  type: nextjs    # Port 3000 automatically
backend:
  type: fastapi   # Port 8000 automatically

Database images become managed services. Instead of specifying image: postgres:16 and manually configuring volumes, passwords, and ports, you declare type: postgres. Hostess provisions the database, generates credentials, and exposes connection details through magic variables. Add backups: when you want scheduled backups.

db:
  image: postgres:16
  environment:
    - POSTGRES_USER=postgres
    - POSTGRES_PASSWORD=mysecretpassword
    - POSTGRES_DB=myapp
  volumes:
    - postgres_data:/var/lib/postgresql/data
db:
  type: postgres
  resources: medium

Hardcoded connection strings become magic variables. Instead of manually constructing postgresql://postgres:mysecretpassword@db:5432/myapp, you use ${db.url}. Hostess resolves this to a full connection string with the correct hostname, port, and credentials at deploy time.

environment:
  - DATABASE_URL=postgresql://postgres:mysecretpassword@db:5432/myapp
  - REDIS_URL=redis://redis:6379
env:
  DATABASE_URL: ${db.url}
  REDIS_URL: ${redis.url}

Application secrets move to secret references. Hardcoded app secrets and API keys in Docker Compose become secret references. This keeps sensitive values out of your config file and version control.

environment:
  - JWT_SECRET=super-secret-key-change-me
env:
  JWT_SECRET: ${secret:JWT_SECRET}

Storage becomes explicit production storage. For managed databases (type: postgres, type: redis), use retention: permanent and resource storage settings for data you want to keep across deploys. For custom services, named Compose volumes convert to persistence mounts. For bind mounts, choose files.source for read-only config files or persistence.mount for durable service data.

Frontend API URL uses a magic variable. Replace local browser URLs such as http://localhost:8000 with ${backend.external_url} so browser requests use the deployed backend URL.

Add secrets

Create the Hostess project, then set the secret value referenced by ${secret:JWT_SECRET}:

Terminal
hostess projects create my-app
export JWT_SECRET="super-secret-key-change-me"
hostess secrets add JWT_SECRET --envs production
✓ Project 'my-app' created in organization 'my-org'
✓ Secret 'JWT_SECRET' added to production

Keep secrets out of hostess.yml and version control. Use ${secret:NAME} references and set values via the CLI or Studio.

If you have multiple secrets, set them all:

Terminal
hostess secrets add STRIPE_API_KEY --value sk_live_abc123 --envs production
hostess secrets add SENDGRID_API_KEY --value SG.abc123 --envs production

Deploy

Run the deploy command:

Terminal
hostess deploy --env production --no-interactive

Example non-interactive output:

Deploying my-app (production)
Services: backend, db, frontend, redis

Deployment ID: dep_abc123
Status: building
Status: starting services
Service db: ready
Service backend: ready
Service frontend: ready
Service redis: ready
Status: complete

✓ Deployment complete (dep_abc123)
backend: https://my-app-backend-a3x9d2m1.hostess.run
frontend: https://my-app-frontend-k7xm9p2q.hostess.run

→ View deployment: https://hostess.sh/dashboard/org/my-org/projects/proj_abc123/deployments/dep_abc123

Verify the migration

Test that everything works:

  1. Open the frontend URL in your browser — you should see your Next.js app
  2. Check the backend by visiting the API URL — confirm it responds
  3. Verify database connectivity — trigger any action that reads/writes to the database
  4. Check Studio for deployment health:
Terminal
open https://hostess.sh

Quick Reference: Docker Compose to Hostess

Docker ComposeHostessNotes
image: postgres:16type: postgresManaged database; add backups: for scheduled backups
image: redis:7type: redisManaged Redis instance
build: ./appbuild: { source: ./app }Same concept, different syntax
ports: ["3000:3000"]type: nextjsPorts are inferred from service type
environment:env:Same concept, supports magic variables
depends_on:depends_on:Supports service health waits and completed job gates
database volumes:retention and resources.storageChoose data retention and storage size for Postgres and Redis
custom named volumes:persistence:Durable custom-service directories
bind mountsfiles.source or persistence.mountChoose file mounts for read-only config, persistence for durable data
JWT_SECRET=xxx${secret:NAME}Secrets stored securely outside the config
links:Magic variablesService discovery via ${service.url}

What's Next

Migrate from Docker Compose | Hostess Docs