Wdrożenie aplikacji internetowych wielokontynerów z docker komposi i nginx
Wprowadzenie
Modern web applications rarely run a single monolithic process. Instad, they are composted of multiple collaborations: a frontend, an API, a datase, a cache, a message queue, and perhaps a jobr runner. Manually management thee lifecycle of each controler, their networking, and their depensiont, production- lies errone and -consuming. Docker Compose and Nginx together provide a divide a divitable-ted, production- lly solutin for deziinen and deloyinging theme.
Warunki wstępne
Before diving in, ensure you have the following installad on your system:
- Xi1; Xi1; FLT: 0 Xi3; Xi3; Docker Enginee Xi1; Xi1; FLT: 1 Xi3; Xi3; (wersja 20.10 or newer) - Xi1; FLT: 2 Xi3; Xi3; Install Docker Xi1; Xi1; FLT: 3 Xi3; Xion3; FLT: 3 Xion3; Xion3;
- Xi1; Xi1; FLT: 0 Xi3; Xi3; Xi3; Xi1; FLT: 1 Xi3; Xi3; (V2 is now integrated into Docker CLI; verify with Xi1; Xi1; FLT: 0 XI3; XI3;) - 1; FLT: 2 XI3; XI3; VI3; Install Docker Compose Xi1; XI1; FLT: 3 XI3; XI3; FLT: 3;
- Xi1; Xi1; FLT: 0 Xi3; Xi3; Basic familarity Xi1; Xi1; FLT: 1 Xi3; Xi3; With terminal Commands andd YAML syntax.
Opcjonalne have a domayn name pointing to your server if you plan to follow thee SSL configuation section.
Understanding Docker Compose in Depph
Docker Compose tool that lets you define services, networks, volumes, environment variables, restart policies, and health checks in a single file called indiv1; Il 1; FLT: 1 X3; Il; Il; Il.; Il. Compose specification services, networks; Il; Il; Il; Il.
Usługi
Each container image, it s ports, volumes, environment, and dependencies are defined the defined 1; indi.1; FLT: 2 means 3; indirection3; key. Services can communicate with each each text via a dedicated bridge network that Compose creats automatically. This means you never need to open host ports for inter- servie communication - only the reversy proxy neesti to expose ports tte the outside.
Sieci
By default, Compose creats a single network for all services, giving them DNS- based services discvery using the services name as the hostname. For example, a service named for services, for also define for disolation - for instance, putting only the reverse proxy on a public- facing network and keeping dase one nepanders never nal work.
networks:
frontend:
backend:
services:
nginx:
networks:
- frontend
app:
networks:
- frontend
- backend
db:
networks:
- backend
Wulkany
Volumes are thee preferred mechanism for persisting data generated by Docker containers. In Compose, you can declarate named volumes at te top level and d mount them into services. This is essential for datases, file uploads, and any stateful services.
volumes:
db_data:
services:
postgres:
image: postgres:16
volumes:
- db_data:/var/lib/postgresql/data
Środowisko Zmienne
Externalie configuation using environment variables. You can hardcode them im Compose file for development, but for production you should use e.1.; Department 1; FLT: 7 memorial 3; FLT: 7 metrix; 3; files or Docker secrets. Compose automatically y loads a e.1; FLT: 8 metriamorious 3; FLT; 3; file located in theme directory if you don 't specify 1; FLT: 9 metriamorious 3; FLT: 9 metriamorioil 3;
services:
api:
image: my-api:latest
env_file:
- ./api.env
environment:
- NODE_ENV=production
Configuring Nginx as a Reverse Proxy - Advanced
Nginx shines a reverse proxy because of it high performance, low memory footprint, and rich difficure set. In a multicontexe proxy architecture, Nginx sits at thee edge, accepts client requests, and forwards them tem te e appropriate services based on thee request URI, headers, or even body content. It can also handle SSL termition, caching, rate limiting, and load balancing.
Basic Reverse Proxy wigh Multiple Upstreams
Here is a more complete configuation that demonstrantates how to split traffic between two different applications, including ding handling static assets andWebSockets:
upstream app1_upstream {
server app1:8000;
}
upstream app2_upstream {
server app2:8001;
}
server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# App 1 – main web frontend
location / {
proxy_pass http://app1_upstream;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# App 2 – admin dashboard
location /admin/ {
proxy_pass http://app2_upstream/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# WebSocket support (e.g., for live updates)
location /ws/ {
proxy_pass http://app1_upstream;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Static assets – serve directly for better performance
location /static/ {
alias /var/www/static/;
expires 30d;
add_header Cache-Control "public, immutable";
}
}
Uwaga: te wszystkie zasady są dostępne dla 1; 1; FLT: 12 contacts 3; 3; blocks. They allow you tu definie a group of servers for load balancing. Even wigh a single server, using an upstream group makes it easy to add more replicas later with out touching the server block.
SSL / TLS wigh Let 's Encrypt
For production, serving HTTPS is non-difficable. You can automate certificate renewal using Certbot or distribution 1; Xi1; FLT: 0 disable3; acme.sh diplorate 1; FLT: 1 diplorate 3; Xi3; inside a sidecar container. A contaxel is to mount the certificates as volumes into the Nginx contaxer. For example:
services:
nginx:
image: nginx:alpine
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- ./certs:/etc/nginx/certs:ro
- ./static:/var/www/static:ro
ports:
- "80:80"
- "443:443"
depends_on:
- app1
- app2
For automatic renewal, you can add a companion container like indi1; endi1; FLT: 14 contain3; entitle3; or contain1; entitle1; entitle1; FLT: 15 contain3; entitle3; thatruns a cron jobd and reloads Nginx when certificates are refreshed.
Load Balancing i Health Checks
When you scale a service to multiple replicas, Nginx can difficee requests using ronda-robin, least connections, or IP hash. Combinate this witch Docker Compose 's previdence 1; FLT: 16 contributions 3; option:
upstream api_servers {
least_conn;
server api:80 max_fails=3 fail_timeout=30s;
}
To detect unhealty conteners, Nginx can be configured with indi1; Xi1; FLT: 18 X3; Xi3; (requires NGINX Plus or use the Xi1; Xi1; FLT: 19 XI3; XI3; frem the open- source version with a small workaround). A simpler contritiva is to rely on Docker 's health checks and only register contribuers that pass.
Building thee Docker Compose File - A Real-Worlds Example
Let 's combinae everthing into a practical Compose file for a web application consideng of a Node.js API, a React frontend served by by Nginx, a PostgreSQL datase, and a Redis cache. The Nginx container will serve static files and proxy API requests.
version: "3.9"
services:
nginx:
image: nginx:alpine
container_name: reverse-proxy
restart: unless-stopped
volumes:
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./frontend/build:/var/www/frontend:ro
- ./certs:/etc/nginx/certs:ro
ports:
- "80:80"
- "443:443"
depends_on:
- api
- frontend
networks:
- public
frontend:
image: my-frontend:latest
# In production, frontend is built into static files and served by Nginx
# This container may run a dev server, but Nginx will bypass it for static files
container_name: frontend-dev
ports:
- "3000:3000"
networks:
- public
# healthcheck: ...
api:
image: my-api:latest
container_name: api-server
restart: unless-stopped
env_file:
- ./api/.env.production
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_started
networks:
- public
- internal
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:4000/health"]
interval: 30s
timeout: 10s
retries: 3
postgres:
image: postgres:16-alpine
container_name: database
restart: unless-stopped
environment:
POSTGRES_USER: app_user
POSTGRES_DB: app_db
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
volumes:
- pgdata:/var/lib/postgresql/data
secrets:
- db_password
networks:
- internal
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app_user"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
container_name: cache
restart: unless-stopped
volumes:
- redisdata:/data
networks:
- internal
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
volumes:
pgdata:
redisdata:
networks:
public:
internal:
internal: true
secrets:
db_password:
file: ./secrets/db_password.txt
Key points in this file:
- Xiv1; Xi1; FLT: 0 Xiv3; Xiv3; Network isolation Xi1; Xi1; FLT: 1 XI1; XI1; FLT: 21 XI3; XIX3; XIX3; XIX3; XIX1; XIX1; FLT: 22 XIV3; XIV3; XIVE: XIVE XIVE; XIVE; FLT: 21 XIV3; X3; XIX3; XIX3; XIXIVD: XIVD; XIVD; XIVE XIVIVIVIVE; XIVIVIVYVE; XIVYVYVE; XIXIVYVYVE; XIVYVYVEYYVEYVEYQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ@@
- Xi1; Xi1; FLT: 0 Xi3; Xi3; Secret: 1; Xi1; FLT: 1 XI3; Xi3;: Sensitivie data like passwords are stored as Docker secrets, mounted as files inside the container. This avoid requiing them in environment variables that cat be exposed via Xi1; Xi1; FLT: 23 XI3; Xi3;
- Xi1; Xi1; FLT: 0 Xi3; Xi3; Health checks Xi1; Xi1; FLT: 1 Xi3; Xi3;: Services declarate health checks so that Xi1; Xi1; FLT: 24 XI3; Xi3; can wait for the condition Xion1; Xi1; FLT: 25 Xi3; Xi3; Xi3;. Nginx will start only after the API is healty.
- W przypadku gdy w ramach projektu nie ma możliwości zastosowania, należy podać nazwę i adres producenta.
Deploying the Application - Step by Step
With the Compose file and Nginx configuation ready, deployment involves a few simple commands. First, ensure all configuation files are in place:
project/
├── docker-compose.yml
├── nginx/
│ └── nginx.conf
├── api/
│ └── .env.production
├── frontend/
│ └── build/
├── certs/
│ └── (fullchain.pem, privkey.pem)
└── secrets/
└── db_password.txt
Run:
docker compose pull # pull latest images
docker compose up -d # start all services in background
docker compose ps # verify services are running
docker compose logs -f # tail logs for all services
Once everthing is up, tect the application by y visiting your domain. Check Nginx logs to confirm requests are being proxied correctly. If you need to update thee configuration or environment variables, edit the relevant files and run:
docker compose up -d --no-deps --build nginx # rebuild only nginx if config changed
For zero-downtime deployments, consider using presenti1; Sui1; FLT: 0 sui3; Suix 3; Sui1; FLT: 1 sui3; Sui3; Or sui1; Sui1; FLT: 2 suidure 3; Suidu3; rolling updates presenti1; Sui1; FLT: 3 sui3; Suidu3; Witch Compose 's presentione1; Sui1; FLT: 29 suiseend; Suiture and Nginx upstream health checks.
Scaling andd Performance Tuning
Docker Compose makes it trivial to scale stateless services like the API:
docker compose up -d --scale api=3
Nowth three replicas of thee API container are running. Nginx, configured with the upstream block, will automaticaly distribute requests among them. To further enhance performance:
- Xi1; Xi1; FLT: 0 Xi3; Xi3; Enable Gzip compression Xi1; Xi1; FLT: 1 Xi3; Xi3; in Nginx for text- based responses.
- Xi1; Xi1; FLT: 0 Xi3; Xi3; Set proper cache headers Xi1; Xi1; FLT: 1 Xi3; Xi3; for static assets (as shown in the example).
- Xi1; Xi1; FLT: 0 Xi3; Xi3; Use a CDN Xi1; Xi1; FLT: 1 Xi3; Xi3; for global delivy of static files.
- Xi1; Xi1; FLT: 0 Xi3; Xi3; Tone Nginx worker processes Xi1; Xi1; FLT: 1 Xi3; Xi3; tu match CPU cores: Xi1; Xi1; FLT: 31 Xi3; Xi3; Xi3;
- Xiv1; Xiv1; FLT: 0 Xiv3; Xiv3; Vyv3; Vyv3; FLT: Vyv3; FLT: Vyv3; Xiv3; FLT: 32 Xiv3; Xiv3; FLT: Vyv3;
For databases, ensure you have connection pooling in your API and consider using PgBouncer as a sidecar container.
Rozwiązywanie problemów Common Emites
Eun wigh a well-structured setup, things can go wrong. Here are e frequent pitfalls andd how to fix them:
- Xi1; Xi1; FLT: 0 Xi3; Xi3; Container cannote reach anotherr servisie by name Xi1; Xi1; FLT: 1 Xi3; Xi3;: Check that both services are on thee same Docker network. Usie Xi1; Xi1; FLT: 33 Xi3; Xi3; to tect.
- Xiv1; Xi1; FLT: 0 Xi3; Xiv3; Xiv3; Nginx returns 502 Bad Gateway Bis1; Xiv1; FLT: 1 Xiv3; Xiv3;: The upstream container may not be running or is nott listening on thee expected port. Verify with Xiv1; Xi1; FLT: 34 X3; XIv3; Also ensure the proxy _ pass includes thee correct protocol and port.
- BEN1; BEN1; FLT: 0 XI3; BENMISON ERrors with volumes present 1; BEN1; FLT: 1 XI3; FLT: Files mounted into conteners levenit the host 's ownership. Use user namespaces or set the VEN1; FLT: 35 X3; directive in Nginx and the service contener.
- Xi1; Xi1; FLT: 0 Xi3; Xi3; SSL certificate nott resourcing Xi1; Xi1; FLT: 1 Xi3; Xi3;: If using certbot standalone, ensure ports 80 and 443 are nott bloked by Nginx during renewal. Use a webroot methood or certbot with docker tam avoid conflicts.
- Xi1; Xi1; FLT: 0 XI3; XI3; Environmentals variable not loaded 1; XI1; FLT: 1 XI3; XI3;: Verify the XI1; XI1; FLT: 36 XI3; FLT: 36 XI3; file is te e correct location or use the XI1; XI1; FLT: 37 XI3; directivy Xilily. You can print env inside the exicer with XI1; XI1; FLT: 38 X3; X3; XIX3;
Security Bett Practices
Running multicontainer applications in production requires vigilance:
- Xi1; Xi1; FLT: 0 XI3; XI3; Never run containers as root presen1; XI1; FLT: 1 XI3; XI3; unless necessary. Use the XI1; XI1; FLT: 39 XI3; XI3; directive or specify a XI1; XI1; FLT: 40 XI3; XI3; in the Dockerfile.
- Reg.
- Restrict network exposure 1; Restrict 1; FLT: 1 presenta3; Release the reverse proxy ports to thee internet. All teor services should be one on internal networks.
- Xi1; Xi1; FLT: 0 Xi3; Xi3; Usie secrets management Xi1; Xi1; FLT: 1 Xi3; Xi3; FR passwords, API keys, andtokens. Docker secrets are a good start; for larger setups, consider HashiCorp Vault.
- Xi1; Xi1; FLT: 0 Xi3; Xi3; Enable Docker Content Truss Xi1; Xi1; FLT: 1 Xi3; Xi3; tu verify image signaures.
- Xi1; Xi1; FLT: 0 Xi3; Xi3; Regularly logg and audit Xi1; Xi1; FLT: 1 Xi3; Xi3; XiMER activity using Xi1; Xi1; FLT: 41 Xion3; Xion3; or a centralised logging solution like ELK stack.
- Xi1; Xi1; FLT: 0 Xi3; Xi3; XiY resource limits Xi1; Xi1; FLT: 1 Xi3; Xi3; in your Compose file to prevent a single container frem starving others:
services:
api:
deploy:
resources:
limits:
cpus: '0.50'
memory: 256M
reservations:
cpus: '0.25'
memory: 128M
Tese limits are respected when using Docker swarm; for plain Compose, they are enforced witch indi.1; Gior1; FLT: 43 contribution 3; Gior3; (though it 's better to use swarm or K8 s for production orchestration).
Konkluzja
1s; 1s; 1s; 1s; 1s; t; 1s; t; 1s; t; 1s; t; 1s; t; 1s; t; 1s; t; 1s; t; t; 1s; t; t; t; t; 1s; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t; t;