HubDocs

Guides / Deploy

Compose apps. Deploy a compose project with workers and a database.

A folder with a compose file deploys as that project. The CLI names the services it found:

Deploying crm (compose: web, worker, db) to acme

The public service

One service gets the app's URL:

  1. The service with x-hub: { port: 3000 }, naming the port inside the container.
  2. Otherwise, the only service with ports, on the container side of its first mapping.

Two services with x-hub.port are refused. A project with neither gets no URL, which suits a worker.

A database

A database is a service in the same file, with its data in a named volume and its password in the app's environment:

compose.yaml
services:
  web:
    build: .
    ports: ["8080:3000"]
    environment:
      DATABASE_URL: postgres://app:${DB_PASSWORD}@db:5432/app
    depends_on: [db]
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: app
    volumes:
      - dbdata:/var/lib/postgresql/data
volumes:
  dbdata:

Set the password before the first deploy, then open the database when you need it:

hub app crm env set DB_PASSWORD
hub deploy
hub app crm exec --service db -- psql -U app app

Named volumes survive deploys, rollbacks, environment changes, stop and start. hub app crm rm asks whether to delete them. Hub does not back up volumes yet, so keep your own backup of data that matters.

Variables

${NAME} in the file reads the app's environment, set with hub app <name> env set. Hub does not read the .env file in your folder. Services Hub builds get every variable in their environment too; a service that runs a ready image, such as postgres:16-alpine, gets only what the file gives it. See Environment variables.

What Hub changes

  • ports and container_name are dropped, with a line in the build log. Hub routes to the public service itself.
  • Every service gets restart: unless-stopped and init: true, unless it sets them.
  • Bind mounts inside the app folder become read-only. Keep data in named volumes.

What Hub refuses

The CLI checks the file before it uploads, and Hub checks it again before anything builds. A compose file may not reach outside its own project:

  • privileged, devices, gpus, sysctls, runtime, cgroup, cgroup_parent, userns_mode, and cap_add other than NET_BIND_SERVICE.
  • network_mode other than service:<name>, and pid, ipc and uts.
  • security_opt entries with unconfined, apparmor or seccomp, use_api_socket, and volumes_from a container.
  • Builds on the host network, privileged builds, build entitlements and SSH.
  • External networks and volumes, drivers other than bridge and local, driver_opts, and networks or volumes with a fixed name.
  • Any file outside the app folder: bind mounts, build contexts, Dockerfiles, env files, secrets and configs. extends with a file and include are refused too.
  • Service names outside a-z, 0-9, ., _ and -, and uploads with symbolic or hard links.

If a deploy from GitHub fails on an external network or a missing port, Hub can open a pull request that fixes the file. See Deploy from GitHub.

On this page