CRITICAL INFRA
Loading critical CVEs…
ALL EXPLOITED
Loading…

Docker in production with Nginx — reverse proxy, TLS and hardening

Docker solves "works on my machine", but in production you need a controlled entry point: Nginx in front of the containers, with TLS terminated in one place, isolated networks and closed ports. A practical, step-by-step guide with docker-compose.

Why Docker + Nginx

Architecture

The internet reaches only Nginx (80/443). Nginx proxies to the app containers over an internal Docker bridge network. The database is on the same network but publishes NO port to the outside.

Internet443 / 80 Nginxreverse proxy + TLS docker network (bridge, intern) app1 :8080 app2 :3000 db (postgres)fara port public

Step 1 — Install Docker

Step 2 — docker-compose for the app

Example docker-compose.yml with an app + Postgres, healthcheck and restart policy:

Step 3 — Nginx reverse proxy

Nginx (on the host or as a container on the same web network) proxies to the container by service name:

Step 4 — TLS with Let's Encrypt

Step 5 — Hardening

Automatic Let's Encrypt

The certificate should not be renewed by hand. Certbot installs a systemd timer (certbot.timer) that runs certbot renew twice a day and only renews what expires in under 30 days.

GeoIP + nftables firewall

Country blocking can happen at two levels. At the nginx level (GeoIP2 module + map $geoip2_country_code) you reply with 403. It is more efficient to drop packets in nftables before they reach nginx — that also blocks scans, not just HTTP requests.

CrowdSec on Nginx logs

CrowdSec reads the nginx logs (access.log / error.log), applies scenarios and blocks hostile IPs — with collective intelligence (IPs reported across the whole CrowdSec network).

Logging and operations

Conclusion

The Nginx + Docker pattern gives you reproducible deploys, a single TLS entry point and a minimal attack surface — from a single VPS to multiple nodes. We design it, secure it and monitor it for you.

Example: full docker-compose.yml

A full stack (app + db + Nginx) in a single file, ready for docker compose up -d:

# docker-compose.yml — app + Postgres + Nginx, retea interna
services:
  app:
    image: myapp:latest
    restart: unless-stopped
    expose: ["8080"]          # NU 'ports' — nu expune public
    networks: [web]
    depends_on: [db]
  db:
    image: postgres:16
    restart: unless-stopped
    environment: { POSTGRES_PASSWORD: schimba-ma }
    volumes: [dbdata:/var/lib/postgresql/data]
    networks: [web]           # fara 'ports' — doar intern
  nginx:
    image: nginx:alpine
    restart: unless-stopped
    ports: ["80:80", "443:443"]
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - ./certs:/etc/nginx/certs:ro
    networks: [web]
networks: { web: {} }
volumes:  { dbdata: {} }
Let's discuss your project →