Bring a whole stack in one go: deploy a directory that has a docker-compose file, and every service in the file becomes a service in the project.
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 on the canvas.
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: the same idea, ready-made.
| 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 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 made here is: a snapshot 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: 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: 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.
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, 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, as secrets, or the files in the image. env_file isn't read either.