# Compose apps

Source: https://docs.myhub.host/deploy/compose

A Compose file runs multiple services as one app. Deploy it through the same [local or GitHub workflow](https://docs.myhub.host/deploy).

## The public service

One service gets the app URL. Mark its container port with `x-hub: { port: 3000 }`, or give only that service a `ports` mapping. A worker-only project needs neither and gets no URL.

## A database

For an **existing app**, add a database with a named volume. Set its password before deploying this change; environment variables require an app that already exists.

This web + database example defaults to always-on and needs two always-on container slots. Free includes one, so this example needs a higher allowance. Check your organization's capacity with `hub usage` before adding the database; [contact Hub](mailto:vlad@myhub.host) to increase it.

```yaml title="compose.yaml"
services:
  web:
    build: .
    x-hub: { port: 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:?Set DB_PASSWORD in Hub}
      POSTGRES_DB: app
    volumes:
      - dbdata:/var/lib/postgresql/data
volumes:
  dbdata:
```

```sh
hub app my-app env set DB_PASSWORD
hub deploy
```

The password command prompts without echoing it. The required-variable guard prevents database startup without it.

Named volumes survive deployments and rollback of the same instance. &#x2A;*Hub does not back them up automatically.** Keep your own backups; deleting volumes removes their data. Recreating a deleted app or instance name does not restore retained volumes; see [storage and deletion](https://docs.myhub.host/deploy/storage#remove-an-app).

## Variables

`${NAME}` uses Hub's [environment variables](https://docs.myhub.host/environment), not a local `.env`. Services Hub builds receive those variables; ready-image services receive only what Compose explicitly passes.

## Deploy safely

Named volumes hold writable data. Project bind mounts are read-only; host privileges, external networks or volumes, and files outside the project are rejected.

Multi-service apps stay running by default. Only enable sleep when every service can pause between requests. See [runtime and sleep](https://docs.myhub.host/deploy/runtime) for that workflow and the [runtime command](https://docs.myhub.host/cli/app/runtime) for options.

## Runtime policy

Set `x-hub-runtime` at the top level of your Compose file to choose whether the instance can sleep:

```yaml title="compose.yaml"
x-hub-runtime:
  policy: auto_sleep
  activity: http
  idleSeconds: 300
services:
  web:
    build: .
    x-hub: { port: 3000 }
```

| Field         | Values                                         |
| ------------- | ---------------------------------------------- |
| `policy`      | Required: `auto_sleep` or `always_on`.         |
| `activity`    | Required: `http`, `background`, or `stateful`. |
| `idleSeconds` | Integer from 5 to 86400; defaults to 300.      |

`auto_sleep` requires `activity: http` and a public service selected by Compose's port rules. Only use it when every service can pause between requests. Jobs, timers, and queue consumers need `always_on`; use `activity: background` for background work or `stateful` for stateful services. Unknown fields are rejected.

Without this block, a single HTTP service defaults to sleep after five idle minutes; other projects default to always on. On the VM runtime, sleep pauses every service in the instance and preserves its disks. Explicit `x-hub-runtime` settings override runtime policy on redeploy. Inspect or change policy with [`hub app runtime`](https://docs.myhub.host/cli/app/runtime); start a sleeping instance before changing it.