From ec26190cf7a27ae006ed09b98c999c42842b1d39 Mon Sep 17 00:00:00 2001 From: Kristin Martin Date: Fri, 10 Apr 2026 15:51:02 -0700 Subject: [PATCH 1/7] add docker compose section to multicontainer guide --- .../multi-container-machines.html.markerb | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/machines/guides-examples/multi-container-machines.html.markerb b/machines/guides-examples/multi-container-machines.html.markerb index 9829332171..a2e0add1f5 100644 --- a/machines/guides-examples/multi-container-machines.html.markerb +++ b/machines/guides-examples/multi-container-machines.html.markerb @@ -140,6 +140,103 @@ Containers in a Machine share the same kernel and VM, but are isolated at the pr There are several ways to deploy multi-container machines on Fly.io. Choose the method that best fits your workflow: +### Using Docker Compose + +If you already have a Docker Compose setup, you can deploy it to Fly Machines without rewriting your configuration. Fly builds and runs Compose services as containers within a single Machine. + +#### Requirements + +You need flyctl v0.3.149 or later: + +```bash +fly version +``` + +If you're behind, update with `fly version update`. + +#### Configuration + +Add a `[build.compose]` section to your `fly.toml`: + +```toml +[build.compose] +``` + +Fly auto-detects Compose files using the standard lookup order: `compose.yaml`, `compose.yml`, `docker-compose.yaml`, `docker-compose.yml`. If your file has a different name, specify it: + +```toml +[build.compose] +file = "docker-compose.prod.yml" +``` + +#### Routing traffic + +Set `internal_port` in your `fly.toml` `[[services]]` or `[http_service]` block to match the port exposed by the container that should receive traffic. Only one container handles inbound requests from the Fly proxy. + +```toml +[http_service] + internal_port = 8080 + force_https = true +``` + +#### Example + +A rate-limiting setup with nginx in front of a backend app. + +**compose.yml:** + +```yaml +services: + nginx: + image: nginx:latest + ports: + - "8080:8080" + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf:ro + depends_on: + - app + + app: + build: . + expose: + - "3000" +``` + +**fly.toml:** + +```toml +app = "my-rate-limited-app" +primary_region = "ord" + +[build.compose] + +[http_service] + internal_port = 8080 + force_https = true + auto_stop_machines = "stop" + auto_start_machines = true + +[[vm]] + size = "shared-cpu-1x" + memory = "512mb" +``` + +Deploy with: + +```bash +fly deploy +``` + +#### Limitations + +- **One buildable service.** Only one service in the Compose file can specify `build`. All other services must use pre-built images. +- **Secrets are global.** Secrets set with `fly secrets` are available to every container in the Machine. You can't scope a secret to a single service. +- **Environment variable conflicts.** Fly injects runtime environment variables (like `FLY_APP_NAME`, `PRIMARY_REGION`, etc.) into all containers. These can overwrite values you define in your Compose file's `environment` block. + +#### Planned improvements + +The team is working on support for mapping Compose volumes to Fly volumes, replacing database services in Compose files with Fly-managed equivalents (like Fly Postgres), and auto-detecting Compose files during `fly launch`. + ### Using the Machines API You can create multi-container Machines by sending a POST request to the Machines API. For example, using `curl`: From 6786ce158ac1142a78f9c242b398be9e201737cb Mon Sep 17 00:00:00 2001 From: Kristin Martin Date: Mon, 13 Apr 2026 19:43:37 +0000 Subject: [PATCH 2/7] Simplify Docker Compose example to use only pre-built images Replace the nginx + custom-build rate-limiting example with a web service + Redis sidecar using two public images. The previous example referenced an nginx.conf and Dockerfile that weren't shown, so readers couldn't run it as written. --- .../multi-container-machines.html.markerb | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/machines/guides-examples/multi-container-machines.html.markerb b/machines/guides-examples/multi-container-machines.html.markerb index a2e0add1f5..51ff16f2dc 100644 --- a/machines/guides-examples/multi-container-machines.html.markerb +++ b/machines/guides-examples/multi-container-machines.html.markerb @@ -181,31 +181,25 @@ Set `internal_port` in your `fly.toml` `[[services]]` or `[http_service]` block #### Example -A rate-limiting setup with nginx in front of a backend app. +A web service with a Redis sidecar, both running in the same Machine. **compose.yml:** ```yaml services: - nginx: - image: nginx:latest + web: + image: nginxdemos/hello:plain-text ports: - - "8080:8080" - volumes: - - ./nginx.conf:/etc/nginx/nginx.conf:ro - depends_on: - - app - - app: - build: . - expose: - - "3000" + - "8080:80" + + redis: + image: redis:latest ``` **fly.toml:** ```toml -app = "my-rate-limited-app" +app = "my-compose-app" primary_region = "ord" [build.compose] @@ -227,6 +221,8 @@ Deploy with: fly deploy ``` +The `web` container handles inbound traffic on port 8080, and the `redis` container runs alongside it as a sidecar. Your application code can reach Redis at `localhost:6379` because both containers share the same network namespace inside the Machine. + #### Limitations - **One buildable service.** Only one service in the Compose file can specify `build`. All other services must use pre-built images. From a112c880dfddc0ce1244954c57b62015187217a7 Mon Sep 17 00:00:00 2001 From: Kristin Martin Date: Tue, 14 Apr 2026 17:25:55 +0000 Subject: [PATCH 3/7] Require a buildable service in the Docker Compose example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fly deploy needs at least one Dockerfile, buildpack, or image in fly.toml to build from — a compose file with only pre-built images fails with "app does not have a Dockerfile or buildpacks configured." Add a minimal Dockerfile and switch the web service to build: . so the example is runnable as written. Also tighten the limitation wording from "only one" to "exactly one." --- .../multi-container-machines.html.markerb | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/machines/guides-examples/multi-container-machines.html.markerb b/machines/guides-examples/multi-container-machines.html.markerb index 51ff16f2dc..2d4a187ceb 100644 --- a/machines/guides-examples/multi-container-machines.html.markerb +++ b/machines/guides-examples/multi-container-machines.html.markerb @@ -181,14 +181,20 @@ Set `internal_port` in your `fly.toml` `[[services]]` or `[http_service]` block #### Example -A web service with a Redis sidecar, both running in the same Machine. +A web service with a Redis sidecar, both running in the same Machine. `fly deploy` builds the `web` service from a `Dockerfile` in your project directory; `redis` runs as a pre-built sidecar. + +**Dockerfile:** + +```dockerfile +FROM nginxdemos/hello:plain-text +``` **compose.yml:** ```yaml services: web: - image: nginxdemos/hello:plain-text + build: . ports: - "8080:80" @@ -225,7 +231,7 @@ The `web` container handles inbound traffic on port 8080, and the `redis` contai #### Limitations -- **One buildable service.** Only one service in the Compose file can specify `build`. All other services must use pre-built images. +- **Exactly one buildable service.** `fly deploy` requires exactly one service in the Compose file to specify `build`. All other services must use pre-built images. - **Secrets are global.** Secrets set with `fly secrets` are available to every container in the Machine. You can't scope a secret to a single service. - **Environment variable conflicts.** Fly injects runtime environment variables (like `FLY_APP_NAME`, `PRIMARY_REGION`, etc.) into all containers. These can overwrite values you define in your Compose file's `environment` block. From fbf38ac8d499af763b03dd85a4f33cbe93bc0bea Mon Sep 17 00:00:00 2001 From: Kristin Martin Date: Tue, 14 Apr 2026 17:34:10 +0000 Subject: [PATCH 4/7] Fix port mismatch in Docker Compose example The example mapped container port 80 to host port 8080 in compose.yml and set internal_port = 8080 in fly.toml, but nginxdemos/hello listens on 80, so the Fly proxy forwarded traffic to a port nothing was listening on. Align everything on port 80. --- .../guides-examples/multi-container-machines.html.markerb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/machines/guides-examples/multi-container-machines.html.markerb b/machines/guides-examples/multi-container-machines.html.markerb index 2d4a187ceb..d3042f984e 100644 --- a/machines/guides-examples/multi-container-machines.html.markerb +++ b/machines/guides-examples/multi-container-machines.html.markerb @@ -196,7 +196,7 @@ services: web: build: . ports: - - "8080:80" + - "80:80" redis: image: redis:latest @@ -211,7 +211,7 @@ primary_region = "ord" [build.compose] [http_service] - internal_port = 8080 + internal_port = 80 force_https = true auto_stop_machines = "stop" auto_start_machines = true @@ -227,7 +227,7 @@ Deploy with: fly deploy ``` -The `web` container handles inbound traffic on port 8080, and the `redis` container runs alongside it as a sidecar. Your application code can reach Redis at `localhost:6379` because both containers share the same network namespace inside the Machine. +The `web` container handles inbound traffic on port 80, and the `redis` container runs alongside it as a sidecar. Your application code can reach Redis at `localhost:6379` because both containers share the same network namespace inside the Machine. #### Limitations From 4a6d34c6caea73486d6881dd223b1e096c9ea97b Mon Sep 17 00:00:00 2001 From: Kristin Martin Date: Tue, 14 Apr 2026 19:51:40 +0000 Subject: [PATCH 5/7] Correct minimum flyctl version to v0.3.152 The [build.compose] block syntax was introduced in commit ee0ccdb (merged 2025-07-07 19:42 UTC), which first shipped in v0.3.152 (released 2025-07-08). v0.3.149-v0.3.151 only accept the older string-typed compose = "file.yml" field under [build] and reject the [build.compose] block with an unmarshal error. Verified by running fly config validate against each version. --- machines/guides-examples/multi-container-machines.html.markerb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/machines/guides-examples/multi-container-machines.html.markerb b/machines/guides-examples/multi-container-machines.html.markerb index d3042f984e..25031f1d74 100644 --- a/machines/guides-examples/multi-container-machines.html.markerb +++ b/machines/guides-examples/multi-container-machines.html.markerb @@ -146,7 +146,7 @@ If you already have a Docker Compose setup, you can deploy it to Fly Machines wi #### Requirements -You need flyctl v0.3.149 or later: +You need flyctl v0.3.152 or later: ```bash fly version From c64e8ecebbb0faf4012622a9d0a7daef97a05d01 Mon Sep 17 00:00:00 2001 From: Kristin Martin Date: Wed, 15 Apr 2026 16:51:33 +0000 Subject: [PATCH 6/7] Remove Planned improvements section from Docker Compose guide --- .../guides-examples/multi-container-machines.html.markerb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/machines/guides-examples/multi-container-machines.html.markerb b/machines/guides-examples/multi-container-machines.html.markerb index 25031f1d74..3dab7186d0 100644 --- a/machines/guides-examples/multi-container-machines.html.markerb +++ b/machines/guides-examples/multi-container-machines.html.markerb @@ -235,10 +235,6 @@ The `web` container handles inbound traffic on port 80, and the `redis` containe - **Secrets are global.** Secrets set with `fly secrets` are available to every container in the Machine. You can't scope a secret to a single service. - **Environment variable conflicts.** Fly injects runtime environment variables (like `FLY_APP_NAME`, `PRIMARY_REGION`, etc.) into all containers. These can overwrite values you define in your Compose file's `environment` block. -#### Planned improvements - -The team is working on support for mapping Compose volumes to Fly volumes, replacing database services in Compose files with Fly-managed equivalents (like Fly Postgres), and auto-detecting Compose files during `fly launch`. - ### Using the Machines API You can create multi-container Machines by sending a POST request to the Machines API. For example, using `curl`: From eb3d20bee5e20f09cdb14b671bc391428f1571ed Mon Sep 17 00:00:00 2001 From: Kristin Martin Date: Fri, 17 Apr 2026 16:16:57 +0000 Subject: [PATCH 7/7] Add warning about ignored Compose volumes to multi-container guide --- .../guides-examples/multi-container-machines.html.markerb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/machines/guides-examples/multi-container-machines.html.markerb b/machines/guides-examples/multi-container-machines.html.markerb index 3dab7186d0..517b70299a 100644 --- a/machines/guides-examples/multi-container-machines.html.markerb +++ b/machines/guides-examples/multi-container-machines.html.markerb @@ -229,6 +229,10 @@ fly deploy The `web` container handles inbound traffic on port 80, and the `redis` container runs alongside it as a sidecar. Your application code can reach Redis at `localhost:6379` because both containers share the same network namespace inside the Machine. +
+**Warning:** Fly ignores `volumes:` declarations in your Compose file and prints `Warning: Could not read volume file...` at deploy time. The deploy still succeeds, but any data your containers write is stored on an ephemeral overlay that's wiped on restart or redeploy. For persistent storage, attach a [Fly Volume](/docs/volumes/) and mount it via `[mounts]` in `fly.toml`. +
+ #### Limitations - **Exactly one buildable service.** `fly deploy` requires exactly one service in the Compose file to specify `build`. All other services must use pre-built images.