# docker-compose stacks

Bring a whole stack in one go: [deploy a directory](/docs/deploying-code) that has a docker-compose file, and every service in the file becomes a service in the project.

## Deploy one

In the directory with the compose file, run the command from **+ Create → Your code** (or the same command in CI). It prints what it will create, then sends the code:

```
Project: shop
  + web: new service, built from web/Dockerfile, with an address
  + db: new service, image postgres:17, private
  + cache: new service, image redis:7, private
```

Push again later and the services that are there get the new code; a service you added to the file is created; an `image:` you changed is switched. Services you removed from the file are left alone: delete them in the dashboard. Settings you changed in the dashboard (variables, scaling, domains) stay: after the first push the compose file decides what exists and what it's built from, not how it's configured.

`--service <name>` deploys just one service of the file; `--group <name>` shows the new services as one [group](/docs/one-click#groups) on the canvas.

### Profiles

Services in `profiles:` come as `docker compose up` brings them: only when one of their profiles is deployed. `--profile production` deploys the file's services without a profile and those in `production`; several go comma-separated (`--profile production,init`). Without `--profile`, the plan says which services were left out, and in which profiles. A service named with `--service` comes whatever its profiles.

For well-known apps there may already be a [one-click deploy](/docs/one-click): the same idea, ready-made.

## How the file is read

| In the compose file | On Alf |
| --- | --- |
| `build:` | A service built from your code (Dockerfile and context as given, relative to the compose file). |
| `image:` | A service running that image. |
| `ports:` | A public service with an address; the container port is used. HTTPS ends at the edge, so when the file publishes 80 and 443, it's 80. |
| `ports:` only on `127.0.0.1` | Private: the file keeps it to its own machine. [`alf connect`](/docs/cli#your-machine-in-a-project-s-network) reaches it from yours. |
| no `ports:` | A private service, reachable as `name:port` inside the project. |
| `environment:` | Variables, when the service is created (move passwords to secrets afterwards). |
| named `volumes:` | Volumes that keep their data across deploys. |
| a database's image (`postgres`, `mysql`, `mariadb`, `mongo`, `redis` and the like) or data directory, on a volume | Backed up from the start, as a [database](/docs/databases) made here is: a [snapshot](/docs/backups) a day, the last week of them, and a copy off the server. |
| `profiles:` | Left out unless one of its profiles is deployed (see above). |
| anchors and `<<: *defaults` | Merged, as compose does. |
| `command:`, `entrypoint:` | Kept. |
| `deploy.replicas` | Instances. |
| `mem_limit`, `deploy.resources.limits.memory` | Its [size](/docs/usage#sizes): the smallest with at least that much memory. Without one it's s (512 MB). |
| label `smartalfred.public: "false"` | Private even with `ports:`. |
| labels `smartalfred.always_on`, `smartalfred.health_path` | Never sleeps; the path that says it's ready. |
| label `smartalfred.static: <folder>` (or `"true"`) | A [static site](/docs/static-sites): that folder of the built image is served from the edge, and nothing runs. `"true"`: the image's own `LABEL smartalfred.static` names the folder. |
| label `smartalfred.spa: "true"` | A static site in SPA mode: paths that aren't files get `index.html`. |

Private services (databases and the like) are created first, so they're up when the apps start. Services keep their compose names, so `DATABASE_URL=postgres://…@db:5432/app` keeps working.

There's no host environment here, and no `.env` file is read: `${VAR:-default}` takes its default (which may hold variables of its own), and `${VAR}`, `$VAR`, `${VAR:?…}` and `${VAR:+…}` are empty. An image left without its tag or its name that way (`app:${VERSION}`) is refused before anything is made, naming the variable: write the tag into the file, or give it a default. Anything else the file uses that Alf can't do (bind mounts, a build context outside the code) is listed as a warning in the plan.

A bind mount of a folder of your project that the app writes to (`./data:/app/data`) becomes a volume of the same name, so what's written there is kept across deploys. It starts empty: what's in the folder on your machine isn't copied. The project itself (`./`), single files and read-only mounts are code and configuration: put them in the image instead. Paths outside the project (`/var/run/docker.sock`) are skipped.

## What isn't done here

The plan names each of these once, with the services that use it:

- **`depends_on`** isn't waited for. Private services (databases and the like) start first, and an app that starts before what it needs is started again: 10 seconds later, then less often.
- **`healthcheck`** isn't run. For an app, the label `smartalfred.health_path` holds requests until that path answers.
- **`networks:`** are one network: every service reaches every other by its name, including those the file keeps apart. Other names on a network (`aliases:`) aren't kept, and nor are `network_mode`, `extra_hosts` and `links`.
- **`privileged`, `cap_add`, `devices`** and the like aren't used: every container runs [in a virtual machine of its own](/docs/security), with the same settings. `user` isn't either (it runs as its image's user), nor `shm_size`.
- **`secrets:` and `configs:`** aren't read: put their values under [Variables](/docs/variables-and-secrets), as secrets, or the files in the image. `env_file` isn't read either.
