containers array. This feature is currently available through the Machines API and requires using Pilot as the init system. This capability is useful for co-locating services such as your application server, log shippers, metrics collectors, or sidecars.
Common Sidecar Use Cases
Sidecars are a powerful way to co-locate supporting services alongside your application. This pattern is especially helpful for apps built with multi-tenant or per-user architectures, such as AI agents, dev environments, or SaaS dashboards. Some practical uses include:- Metrics and Logs: Run a Prometheus exporter or a log shipper like Vector to forward application logs or metrics without bundling that logic into your app.
- Secrets Agents: Use a sidecar to fetch secrets from a provider like Vault or cloud metadata services. This keeps your app container lightweight and secure.
- Load Smoothing: Use nginx or Envoy as a reverse proxy for rate limiting, retries, or graceful error handling. This is great for handling bursts in traffic or external API instability.
- Storage or Sync Layers: Mount and manage local state using sidecars like LiteFS or file sync daemons. You can keep persistent logic separated from your app’s logic.
- AI Agents or Workers: For agent-based or queued workloads, you can spin up workers or schedulers in separate containers and coordinate them using
depends_on.
containers array. Each container runs in its own isolated process tree, and Pilot ensures the startup order and health status are respected. While containers share the same kernel and VM, they are isolated at the process and filesystem level.
Defining Containers
When creating a Machine via the API, you can specify acontainers array, each containing a ContainerConfig. Each container can have its own image, environment variables, health checks, startup commands, attached files, and dependencies.
flyd (our in-house orchestrator) handles pulling images and making them accessible to Pilot, the init system responsible for running the containers and managing their lifecycle. Pilot builds a dependency graph using container startup conditions and runs containers accordingly.
Example Configuration
log-shipper container depends on the my-app container being healthy before starting.
Container Dependencies
You can define dependencies between containers using thedepends_on field. Supported conditions include:
condition:
healthy: Container will wait until the dependency container passes its health checks.started: Container will start as soon as the dependency container has started.exited_successfully: Container will wait until the dependency container has exited with a success code.
Health Checks
Each container specifies its own health checks. The system supports different types of health checks: TCP health checks: Checks if a port is accepting connectionsHealth Check Configuration Options
name: string - Unique identifier for the health checkinterval: int - How often to run the check, in secondsgrace_period: int - Time in seconds to wait after container starts before checks begintimeout: int - Maximum time in seconds to wait for a check to completesuccess_threshold: int - Number of consecutive successful checks required to change status to healthyfailure_threshold: int - Number of consecutive failed checks before marking as unhealthy
Security Considerations
Containers in a Machine share the same kernel and VM, but are isolated at the process and filesystem level. Failures in one container won’t directly crash others, but they don’t provide the same level of isolation as across VMs.Deploying Multi-container Machines
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.152 or later:fly version update.
Configuration
Add a[build.compose] section to your fly.toml:
compose.yaml, compose.yml, docker-compose.yaml, docker-compose.yml. If your file has a different name, specify it:
Routing traffic
Setinternal_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.
Example
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:
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
- Exactly one buildable service.
fly deployrequires exactly one service in the Compose file to specifybuild. All other services must use pre-built images. - Secrets are global. Secrets set with
fly secretsare 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’senvironmentblock.
Using the Machines API
You can create multi-container Machines by sending a POST request to the Machines API. For example, usingcurl:
api-config.json file contains the request body for creating a Machine. Unlike the flyctl examples in the next sections, the API expects the JSON body to have a config wrapper with the containers array nested inside:
config object can also include other Machine fields like guest and services.
Refer to the Machines API documentation for complete configuration options.
Using fly machine run
Create a multi-container Machine by passing a JSON configuration file (e.g., cli-config.json) to fly machine run:
containers at the root level, alongside any additional Machine configuration:
Note: When using
--machine-config, your JSON file must include a containers array with at least one container that specifies an image.Using fly launch & fly deploy
Starting with flyctl v0.3.147, you can run multiple containers using fly deploy. This example demonstrates using a nginx container as a rate limiter and a custom app container.
1. Create cli-config.json
2. Update fly.toml
Note:flyctl version 0.3.147 or later is required to use this config
fly deploy.
3. Run fly deploy
echo container, runs both containers on the same machine, and configures traffic through nginx.
You can also inline the machine_config in your fly.toml using triple quotes. The first character inside the string must be {:
Example Configurations
Here are some practical examples of multi-container setups you can use as a starting point:Rate Limiter Sidecar
To mitigate excessive traffic, you can run an nginx container as a sidecar to limit requests per second using thengx_http_limit_req_module. Below is a simplified container configuration that enables rate limiting:
raw_value field must contain base64-encoded content. The nginx configuration shown in the JSON above corresponds to this configuration:
guest_path.
To complete this configuration, you should also include:
- A
servicesblock that exposes internal port 8080 for Fly Proxy - A
guestblock specifying resources like CPU and memory - Optionally,
depends_onfields or health checks if you want nginx to wait for the app
LiteFS Sidecar with Health Checks
You can also run LiteFS as a sidecar with a health check configured to ensure it’s operating correctly:For more information about container configuration options, refer to the Machines API documentation.