Docker 43 🐳 Scaling Compose Services, Dynamic Load Balancing, and Environment Variable Substitution
A Compose service is an abstract definition of a container. When the service is started, Compose creates one container for it. The scale option and the --scale flag create multiple containers for the same service, and Docker’s embedded DNS server load-balances the requests across them. The scaling is horizontal: more containers of the same service, each with the same configuration, behind the same service name. The caller uses the service name as the hostname, and Docker resolves it to one of the containers, rotating through them on each lookup.
The DNS round-robin is the load-balancing mechanism. When a service has multiple replicas, Docker’s DNS server returns all the IP addresses of the replicas, in a rotating order. The client’s resolver picks the first address, and the next lookup returns a different order. The rotation distributes the requests across the replicas, and it is transparent to the caller: the service name is the only thing the client needs to know. The mechanism is not a full load balancer — it does not check the health of the replicas, and it does not consider the current load — but it is sufficient for stateless services and for development.
The environment variable substitution is the mechanism that makes a Compose file portable. The ${VAR} syntax interpolates a host environment variable, and the ${VAR:-default} syntax provides a fallback. The .env file is read automatically, and its variables are available for interpolation. The --env-file flag loads a different file, and the environment and env_file keys in the service set the variables that are passed to the container. The precedence between the sources is defined by the specification, and understanding it is the key to avoiding surprises.
This chapter covers three areas. First, why scaling and load balancing exist — the horizontal-scaling problem and the DNS round-robin mechanism. Second, how to scale a service — the --scale flag, the scale key, the port conflict that scaling creates, and the stateless-service requirement. Third, how environment variable substitution works — the ${VAR} syntax, the .env file, the --env-file flag, the environment and env_file keys, and the precedence rules. The chapter ends with a complete example session, a quick reference, best practices, common pitfalls, real-world examples, and diagrams showing the scaling and substitution models.
Key point: docker compose up --scale web=3 creates three containers for the web service. The service name resolves to all the replica IPs, and Docker’s DNS server rotates the order, distributing the requests. Scaling requires the service to be stateless and the port mapping to be removed from the scaled service. Environment variable substitution uses ${VAR} and ${VAR:-default}, with the .env file read automatically, and the precedence is environment > env_file > host environment.
Why scaling and load balancing exist
The horizontal-scaling problem. A single container has a fixed amount of CPU and memory. When the load increases, the container cannot handle it, and the response time grows. The solution is to run multiple containers of the same service and distribute the requests across them. This is horizontal scaling, and it is the standard approach for stateless services. The alternative — vertical scaling — is to give the single container more resources, which is limited by the host’s capacity and requires a restart.
The load-balancing problem. When there are multiple replicas, the caller needs a way to reach one of them. The caller could be given a list of IP addresses, but the addresses change when the replicas are recreated, and the caller would need to implement the rotation. The service name is the stable identifier, and Docker’s DNS server resolves it to the replica IPs. The caller uses the service name, and Docker handles the resolution and the rotation.
The DNS round-robin mechanism. When a service has multiple replicas, Docker’s embedded DNS server returns all the replica IP addresses in a rotating order. The first lookup returns the addresses in one order, the second lookup returns them in a different order, and so on. The client’s resolver picks the first address, so consecutive lookups distribute the requests across the replicas. The mechanism is simple and transparent, and it works for any client that uses DNS to resolve the service name.
The stateless-service requirement. The DNS round-robin works only for stateless services. A stateful service — a service that stores session data in the container’s memory or in a local file — cannot be scaled, because the state is not shared across the replicas. A request that hits one replica and stores a session, and then hits another replica on the next request, will not find the session. The solution is to move the state to a shared store — a database, a cache, or a shared volume — so that all the replicas can access it.
The port-conflict problem. A scaled service cannot use a fixed host port. If the web service maps 8080:80 and there are three replicas, all three try to bind to host port 8080, and two of them fail. The fix is to remove the host port mapping from the scaled service and put it on a load balancer or a reverse proxy in front of the replicas. The proxy maps the host port to the service name, and Docker’s DNS resolves the service name to the replicas. The proxy distributes the requests across the replicas.
The trade-off. The DNS round-robin is a simple load-balancing mechanism, but it is not a full load balancer. It does not check the health of the replicas, so a request may be routed to a replica that is unhealthy. It does not consider the current load, so a slow replica receives the same share as a fast one. It does not support sticky sessions, so a client that needs to reach the same replica on every request cannot use it. For development and for simple stateless services, the round-robin is sufficient. For production, a real load balancer — nginx, HAProxy, Traefik, or a cloud load balancer — is the correct choice.
a. Scaling a service
A service is scaled with the --scale flag on the up command, or with the scale key in the Compose file. The flag overrides the key.
docker compose up -d --scale web=3
The command creates three containers for the web service. The containers are named myapp-web-1, myapp-web-2, and myapp-web-3, and they all use the same image and configuration. The service name web resolves to all three IP addresses, and Docker’s DNS server rotates the order.
The scale key in the Compose file sets the default number of replicas:
services:
web:
image: nginx:alpine
scale: 3
The scale key is the equivalent of the --scale flag, and the flag overrides the key. The deploy.replicas key is the Swarm equivalent, and it is ignored by plain Docker Compose.
The port mapping is the constraint. A scaled service cannot bind a fixed host port, because the replicas would conflict. The port mapping must be removed from the scaled service and placed on a proxy:
services:
proxy:
image: nginx:alpine
ports:
- "8080:80"
depends_on:
- web
web:
image: myapp
# no ports — the proxy handles the host port
The proxy service maps the host port and forwards the requests to the web service. The web service has no host port mapping, so the replicas do not conflict. The proxy’s configuration resolves the web service name, and Docker’s DNS server returns the replica IPs.
The docker compose up --scale web=3 command creates the proxy and the three replicas. The proxy is a single container, and the replicas are three containers. The proxy forwards the requests to the replicas, and the DNS round-robin distributes them.
The scaled service is stateless. The replicas share no state, and any replica can handle any request. If the service needs session state, the state must be stored in a shared store — a Redis cache, a database, or a shared volume — so that all the replicas can access it.
b. Environment variable substitution
The ${VAR} syntax interpolates a host environment variable. The value is substituted before the Compose file is parsed, so the interpolation applies to every key, not just environment.
services:
api:
image: myapi:${TAG:-latest}
environment:
DATABASE_URL: postgres://app:${DB_PASSWORD}@db:5432/appdb
LOG_LEVEL: ${LOG_LEVEL:-info}
The TAG variable is substituted in the image name. If it is unset, the latest default is used. The DB_PASSWORD variable is substituted in the DATABASE_URL. The LOG_LEVEL variable is substituted with the info default.
The substitution syntax has several forms:
| Syntax | Meaning |
|---|---|
${VAR} | The value of VAR; empty if unset |
${VAR:-default} | The value of VAR, or default if unset or empty |
${VAR-default} | The value of VAR, or default if unset |
${VAR:?error} | The value of VAR, or an error if unset or empty |
${VAR?error} | The value of VAR, or an error if unset |
${VAR:+replacement} | replacement if VAR is set, empty otherwise |
${VAR+replacement} | replacement if VAR is set, empty otherwise |
The :- and - forms differ in the treatment of an empty value. The :- form uses the default if the variable is unset or empty. The - form uses the default only if the variable is unset. The :? form fails with an error if the variable is unset or empty, which is useful for required variables.
The .env file is read automatically from the project directory. The variables in the file are available for interpolation, and they are not passed to the containers unless they are listed in the environment or env_file keys.
# .env
DB_PASSWORD=secret
LOG_LEVEL=debug
TAG=1.2.3
The --env-file flag loads a different file:
docker compose --env-file .env.production up -d
The environment key sets the variables that are passed to the container. The env_file key loads the variables from a file. The precedence between the sources is defined by the specification.
The precedence, from highest to lowest:
- The
environmentkey in the service. - The
env_filekey in the service. - The host environment and the
.envfile interpolation.
The environment key wins over the env_file, and the env_file wins over the host environment. The .env file is used for interpolation, not for passing variables to the container, unless the variables are listed in the environment or env_file keys.
c. The scaling and substitution patterns
The scaling pattern is: a stateless service, no host port mapping, a proxy in front, and a shared store for the state.
services:
proxy:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- web
web:
image: myapp
environment:
REDIS_URL: redis://cache:6379
depends_on:
- cache
cache:
image: redis:7-alpine
The proxy service handles the host port and forwards to the web service. The web service stores its session state in the cache service, so the replicas share it. The web service has no host port mapping, so the replicas do not conflict.
The nginx.conf for the proxy uses the service name as the upstream:
upstream web {
server web:3000;
}
server {
listen 80;
location / {
proxy_pass http://web;
}
}
The web upstream resolves to the replica IPs, and nginx load-balances the requests across them. The server web:3000 line uses the service name, and Docker’s DNS server returns the replica IPs.
The substitution pattern is: the .env file for the defaults, the environment key for the overrides, and the :? form for the required variables.
services:
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD is required}
POSTGRES_DB: ${DB_NAME:-appdb}
The DB_PASSWORD variable is required, and the :? form fails with an error if it is unset. The DB_NAME variable has a default, and the :- form uses appdb if it is unset. The .env file provides the values, and the environment key passes them to the container.
Complete Example Session
# ============================================
# PART 1: A SCALABLE SERVICE
# ============================================
services:
web:
image: myapp
environment:
REDIS_URL: redis://cache:6379
cache:
image: redis:7-alpine
# The web service has no host port mapping.
# It can be scaled to multiple replicas.
# The cache is shared across the replicas.
# ============================================
# PART 2: THE PROXY IN FRONT
# ============================================
services:
proxy:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- web
web:
image: myapp
cache:
image: redis:7-alpine
# The proxy handles the host port.
# The web service is behind the proxy.
# The proxy forwards to the web service name.
# ============================================
# PART 3: THE NGINX CONFIGURATION
# ============================================
# nginx.conf
# upstream web {
# server web:3000;
# }
#
# server {
# listen 80;
# location / {
# proxy_pass http://web;
# }
# }
# The upstream resolves to the replica IPs.
# nginx load-balances the requests across them.
# ============================================
# PART 4: SCALING WITH THE FLAG
# ============================================
# $ docker compose up -d --scale web=3
#
# [+] Running 5/5
# ✔ Container myapp-cache-1 Started
# ✔ Container myapp-web-1 Started
# ✔ Container myapp-web-2 Started
# ✔ Container myapp-web-3 Started
# ✔ Container myapp-proxy-1 Started
# ============================================
# PART 5: THE DNS ROUND-ROBIN
# ============================================
# $ docker compose exec proxy nslookup web
# Name: web
# Address 1: 172.18.0.2 myapp-web-1
# Address 2: 172.18.0.3 myapp-web-2
# Address 3: 172.18.0.4 myapp-web-3
#
# The DNS server returns all three IPs.
# The order rotates on each lookup.
# ============================================
# PART 6: THE SCALE KEY
# ============================================
services:
web:
image: myapp
scale: 3
# The scale key sets the default number of replicas.
# The --scale flag overrides it.
# ============================================
# PART 7: ENVIRONMENT VARIABLE SUBSTITUTION
# ============================================
services:
api:
image: myapi:${TAG:-latest}
environment:
DATABASE_URL: postgres://app:${DB_PASSWORD}@db:5432/appdb
LOG_LEVEL: ${LOG_LEVEL:-info}
# ============================================
# PART 8: THE .env FILE
# ============================================
# .env
# TAG=1.2.3
# DB_PASSWORD=secretpassword
# LOG_LEVEL=debug
# The .env file is read automatically.
# Its variables are available for interpolation.
# ============================================
# PART 9: THE REQUIRED VARIABLE
# ============================================
services:
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD is required}
POSTGRES_DB: ${DB_NAME:-appdb}
# The DB_PASSWORD variable is required.
# The :? form fails with an error if it is unset.
# The DB_NAME variable has a default.
# ============================================
# PART 10: THE COMPLETE STACK
# ============================================
name: scalable-app
services:
proxy:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
web:
condition: service_healthy
networks:
- frontend
web:
image: myapp:${TAG:-latest}
environment:
REDIS_URL: redis://cache:6379
DATABASE_URL: postgres://app:${DB_PASSWORD:?DB_PASSWORD is required}@db:5432/appdb
LOG_LEVEL: ${LOG_LEVEL:-info}
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/health"]
interval: 10s
timeout: 5s
retries: 5
start_period: 20s
networks:
- frontend
- backend
scale: 3
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD is required}
POSTGRES_DB: ${DB_NAME:-appdb}
volumes:
- db-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d appdb"]
interval: 5s
timeout: 5s
retries: 5
start_period: 10s
networks:
- backend
cache:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
networks:
- backend
volumes:
db-data:
networks:
frontend:
backend:
internal: true
The ten parts show a scalable service, the proxy, the nginx configuration, scaling with the flag, the DNS round-robin, the scale key, environment variable substitution, the .env file, the required variable, and the complete stack.
Quick Reference
Scaling
| Command / Key | Effect |
|---|---|
docker compose up --scale web=3 | Create 3 replicas of web |
scale: 3 | Default replica count in the file |
deploy.replicas | Swarm equivalent; ignored by Compose |
| No host port | Required for scaling |
| Proxy in front | Handles the host port |
| Stateless service | Required for scaling |
DNS Round-Robin
| Aspect | Behavior |
|---|---|
| Service name | Resolves to all replica IPs |
| Order | Rotates on each lookup |
| Client | Picks the first address |
| Health check | Not performed by DNS |
| Load awareness | Not performed by DNS |
Substitution Syntax
| Syntax | Meaning |
|---|---|
${VAR} | Value; empty if unset |
${VAR:-default} | Default if unset or empty |
${VAR-default} | Default if unset |
${VAR:?error} | Error if unset or empty |
${VAR:+replacement} | Replacement if set |
Environment Precedence
| Priority | Source |
|---|---|
| 1 (highest) | environment key in the service |
| 2 | env_file key in the service |
| 3 (lowest) | Host environment and .env interpolation |
The .env File
| Aspect | Behavior |
|---|---|
| Location | Project directory |
| Read | Automatically |
| Purpose | Interpolation, not container variables |
| Override | --env-file flag |
Best Practices
✅ Do This:
# Remove the host port from a scaled service
web:
image: myapp
# no ports # ✅
# Put a proxy in front
proxy:
ports:
- "8080:80" # ✅
# Use a shared store for session state
environment:
REDIS_URL: redis://cache:6379 # ✅
# Use the :? form for required variables
POSTGRES_PASSWORD: ${DB_PASSWORD:?required} # ✅
# Use the :- form for defaults
LOG_LEVEL: ${LOG_LEVEL:-info} # ✅
# Use the .env file for environment-specific values
# .env: DB_PASSWORD=secret # ✅
❌ Don’t Do This:
# Don't map a host port on a scaled service
web:
ports:
- "8080:80" # conflict with replicas # ❌
# Don't scale a stateful service without a shared store
web:
scale: 3 # session state lost # ❌
# Don't rely on the DNS round-robin for production
# Use a real load balancer # ❌
# Don't hardcode secrets in the Compose file
POSTGRES_PASSWORD: secret # ❌
# Don't forget the .env file is for interpolation
# List variables in environment to pass them to the container # ❌
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Port conflict on scale | Fixed host port | Remove the port, use a proxy |
| Session state lost | Stateful service scaled | Use a shared store |
| DNS returns one IP | Service not scaled | Scale the service |
| Unhealthy replica receives requests | DNS does not check health | Use a load balancer with health checks |
| Variable not substituted | Not in .env or host | Add the variable or a default |
| Required variable missing | No :? form | Add the :? form |
| Container variable not set | Not in environment or env_file | Add the variable |
Real-World Examples
1. Scale a Service
docker compose up -d --scale web=3
2. The Scale Key
web:
scale: 3
3. The Proxy
proxy:
image: nginx
ports:
- "8080:80"
4. The Nginx Upstream
upstream web {
server web:3000;
}
5. Substitution
image: myapi:${TAG:-latest}
6. Required Variable
POSTGRES_PASSWORD: ${DB_PASSWORD:?required}
7. The .env File
# .env
DB_PASSWORD=secret
8. The env_file Key
env_file:
- .env
9. The Environment Key
environment:
LOG_LEVEL: ${LOG_LEVEL:-info}
10. The Complete Stack
web:
scale: 3
environment:
REDIS_URL: redis://cache:6379
Visual
The Scaling and Load-Balancing Model
┌──────────────────────────────────────────────────────────────┐
│ SCALING AND LOAD-BALANCING │
│ │
│ Client │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ proxy (nginx) │ │
│ │ ports: 8080:80 │ │
│ │ │ │
│ │ upstream web { server web:3000; } │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ │ resolves "web" to the replica IPs │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ web service (3 replicas) │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ web-1 │ │ web-2 │ │ web-3 │ │ │
│ │ │ 172.18.2 │ │ 172.18.3 │ │ 172.18.4 │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ │ shared state │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ cache (redis) │ │
│ │ shared session store │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ The proxy handles the host port. │
│ The replicas share the cache. │
│ The service name resolves to the replica IPs. │
│ │
└──────────────────────────────────────────────────────────────┘
The DNS Round-Robin
┌──────────────────────────────────────────────────────────────┐
│ THE DNS ROUND-ROBIN │
│ │
│ Lookup 1: nslookup web │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ web → 172.18.0.2 (web-1) │ │
│ │ 172.18.0.3 (web-2) │ │
│ │ 172.18.0.4 (web-3) │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ Lookup 2: nslookup web │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ web → 172.18.0.3 (web-2) │ │
│ │ 172.18.0.4 (web-3) │ │
│ │ 172.18.0.2 (web-1) │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ Lookup 3: nslookup web │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ web → 172.18.0.4 (web-3) │ │
│ │ 172.18.0.2 (web-1) │ │
│ │ 172.18.0.3 (web-2) │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ The order rotates on each lookup. │
│ The client picks the first address. │
│ The requests are distributed across the replicas. │
│ │
│ Note: the DNS server does not check health or load. │
│ Use a real load balancer for production. │
│ │
└──────────────────────────────────────────────────────────────┘
Environment Variable Substitution
┌──────────────────────────────────────────────────────────────┐
│ ENVIRONMENT VARIABLE SUBSTITUTION │
│ │
│ Sources: │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ .env file (project directory) │ │
│ │ DB_PASSWORD=secret │ │
│ │ LOG_LEVEL=debug │ │
│ │ │ │
│ │ Host environment │ │
│ │ export TAG=1.2.3 │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Interpolation in the Compose file: │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ image: myapi:${TAG:-latest} │ │
│ │ → myapi:1.2.3 │ │
│ │ │ │
│ │ LOG_LEVEL: ${LOG_LEVEL:-info} │ │
│ │ → debug │ │
│ │ │ │
│ │ POSTGRES_PASSWORD: ${DB_PASSWORD:?required} │ │
│ │ → secret │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Passed to the container: │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ environment: │ │
│ │ LOG_LEVEL: debug │ │
│ │ DATABASE_URL: postgres://app:secret@db:5432/appdb │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────┘
Environment Precedence
┌──────────────────────────────────────────────────────────────┐
│ ENVIRONMENT PRECEDENCE │
│ │
│ Highest: environment key │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ environment: │ │
│ │ NODE_ENV: production │ │
│ │ │ │
│ │ This value wins. │ │
│ └──────────────────────────────────────────────────────┘ │
│ ▲ │
│ │ │
│ Middle: env_file key │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ env_file: │ │
│ │ - .env │ │
│ │ │ │
│ │ This value is overridden by environment. │ │
│ └──────────────────────────────────────────────────────┘ │
│ ▲ │
│ │ │
│ Lowest: host environment and .env interpolation │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ DATABASE_URL: ${DATABASE_URL} │ │
│ │ │ │
│ │ The host variable or .env value is used. │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
--scale web=3 | Creates 3 replicas |
scale: 3 | Default replica count |
| DNS round-robin | Rotates the replica IPs |
| Host port | Must be removed from a scaled service |
| Proxy | Handles the host port |
| Shared store | Required for session state |
${VAR} | Interpolation |
${VAR:-default} | Default if unset or empty |
${VAR:?error} | Error if unset or empty |
.env file | Read automatically for interpolation |
Key takeaways:
docker compose up --scale web=3creates three containers for thewebservice. The service name resolves to all three IP addresses, and Docker’s DNS server rotates the order, distributing the requests across the replicas.- The DNS round-robin is a simple load-balancing mechanism. It returns all the replica IPs in a rotating order, and the client picks the first one. It does not check health or load, so a real load balancer is the correct choice for production.
- A scaled service cannot map a fixed host port. The replicas would conflict. The port mapping must be removed from the scaled service and placed on a proxy or a load balancer in front of the replicas.
- A scaled service must be stateless. The replicas share no state, so a session stored in one replica is not visible to the others. The state must be moved to a shared store — a cache, a database, or a shared volume.
- The
${VAR}syntax interpolates a host environment variable. The${VAR:-default}form provides a fallback, and the${VAR:?error}form fails with an error if the variable is unset or empty. - The
.envfile is read automatically from the project directory. Its variables are available for interpolation, and they are not passed to the container unless they are listed in theenvironmentorenv_filekeys. - The environment precedence is:
environment>env_file> host environment. Theenvironmentkey wins over theenv_file, and theenv_filewins over the host environment and the.envinterpolation. - The
--env-fileflag loads a different file. It is useful for environment-specific configurations, such as.env.productionor.env.staging.
Remember: Scaling a Compose service creates multiple replicas behind the same service name, and Docker’s DNS server rotates the replica IPs to distribute the requests. The scaled service must be stateless and must not map a fixed host port; a proxy handles the host port, and a shared store holds the session state. Environment variable substitution uses the ${VAR} syntax, with the .env file read automatically and the environment and env_file keys passing the variables to the container. The precedence is environment over env_file over the host environment. The result is a stack that scales horizontally, distributes the load, and configures itself from the environment.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!