DEV Community

Cover image for Automating Zero-Downtime Multi-Domain SSL on AWS EC2 with Docker and Nginx
Oyoh Edmond
Oyoh Edmond

Posted on

Automating Zero-Downtime Multi-Domain SSL on AWS EC2 with Docker and Nginx

Automating Zero-Downtime Multi-Domain SSL on AWS EC2 with Docker and Nginx

Managing SSL/TLS certificates across multiple customer subdomains or microservices is a rite of passage for every cloud engineer. While AWS Application Load Balancer (ALB) paired with AWS Certificate Manager (ACM) is the standard managed solution, smaller projects, staging clusters, and cost-conscious architectures often require running an Nginx reverse proxy directly on an EC2 instance.

In this guide, we will implement an automated, multi-domain SSL architecture on an AWS EC2 instance using Docker, Nginx, and Certbot (Let's Encrypt)β€”with zero downtime for existing traffic.

πŸ—οΈ The Architecture Overview

Here is how traffic flows through our host:

graph TD
    Client[Internet Clients] -->|HTTPS :443| Route53[AWS Route 53]
    Route53 -->|A Record| EIP[Elastic IP]
    EIP -->|Port 80/443| EC2[Ubuntu 22.04 EC2 Instance]

    subgraph "Docker Host"
        EC2 --> NginxProxy[Docker: Nginx Reverse Proxy]
        NginxProxy -->|upstream app| ServiceA[Application Container :8000]
        NginxProxy -->|upstream portal| ServiceB[Portal Container :3000]
        Certbot[Certbot Standalone] -.->|expands certs| SSLVolume[(Shared SSL Certs Volume)]
        SSLVolume --> NginxProxy
    end

πŸ› οΈ Step 1: Network & Security Group Baseline

Ensure your EC2 instance is placed in a public subnet with an Elastic IP (EIP). The security group must allow inbound traffic on:

  • Port 80 (HTTP): Required for ACME HTTP-01 domain validation challenges and HTTP-to-HTTPS redirection.
  • Port 443 (HTTPS): Encrypted client traffic.
  • Port 22 (SSH): Strictly restricted to your trusted IP address.

🐳 Step 2: Setting Up the Docker Reverse Proxy

Create a directory structure on your EC2 host:

mkdir -p /opt/cloud-proxy/{nginx/conf.d,ssl,certbot}
cd /opt/cloud-proxy
Enter fullscreen mode Exit fullscreen mode

Create a docker-compose.yml file:

version: '3.8'

services:
  proxy:
    image: nginx:alpine
    container_name: cloud_proxy
    restart: always
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
      - ./ssl:/etc/nginx/ssl:ro
    networks:
      - web_net

  backend_app:
    image: traefik/whoami
    container_name: app_service
    restart: always
    networks:
      - web_net

networks:
  web_net:
    driver: bridge
Enter fullscreen mode Exit fullscreen mode

πŸ“œ Step 3: Nginx Multi-Domain Configuration

Create /opt/cloud-proxy/nginx/conf.d/app.conf:

# HTTP - Redirect all traffic to HTTPS
server {
    listen 80;
    server_name app.example.com portal.example.com;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

# HTTPS - Secure Reverse Proxy
server {
    listen 443 ssl http2;
    server_name app.example.com portal.example.com;

    ssl_certificate /etc/nginx/ssl/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/privkey.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    # Optimized Buffer Configuration for High-Throughput APIs
    proxy_buffering on;
    proxy_buffer_size 16k;
    proxy_buffers 8 32k;
    proxy_busy_buffers_size 64k;

    location / {
        proxy_pass http://backend_app:80;
        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;
    }
}
Enter fullscreen mode Exit fullscreen mode

πŸ” Step 4: Expanding Multi-Domain SSL Certificates

When onboarding a new subdomain (e.g. adding portal.example.com to an existing certificate for app.example.com), Certbot supports expanding Subject Alternative Names (SAN):

# 1. Briefly pause proxy to release port 80/443 for standalone verification
docker stop cloud_proxy

# 2. Expand certificate across all required subdomains
sudo certbot certonly --standalone --expand \
  -d app.example.com \
  -d portal.example.com \
  --non-interactive \
  --agree-tos \
  -m admin@example.com

# 3. Copy newly generated certificates to the proxy SSL directory
sudo cp /etc/letsencrypt/live/app.example.com/fullchain.pem /opt/cloud-proxy/ssl/
sudo cp /etc/letsencrypt/live/app.example.com/privkey.pem /opt/cloud-proxy/ssl/

# 4. Resume proxy
docker start cloud_proxy
Enter fullscreen mode Exit fullscreen mode

Note: The proxy pause takes less than 5 seconds. In production environments where even 5 seconds of downtime is unacceptable, use the Webroot ACME challenge method or DNS-01 validation via AWS Route 53.


πŸ” Step 5: Automated Verification & Health Checks

Always verify your redirects and TLS handshakes with curl:

# Verify HTTP-to-HTTPS 301 Redirect
curl -sD - http://app.example.com | grep -i "Location:"

# Verify SSL Certificate Validity and Expiry
echo | openssl s_client -servername app.example.com -connect app.example.com:443 2>/dev/null | openssl x509 -noout -dates -subject
Enter fullscreen mode Exit fullscreen mode

⚠️ Common Gotcha: Nginx Upstream Temp Buffering

In high-throughput microservices, you may encounter this warning in your Nginx container logs:

[warn] an upstream response is buffered to a temporary file /var/cache/nginx/proxy_temp/...
Enter fullscreen mode Exit fullscreen mode

This occurs when upstream API responses exceed default 4KB memory buffers, forcing Nginx to write temporary files to disk. On AWS EC2, disk I/O on EBS volumes introduces latency spikes. Tuning proxy_buffer_size 16k; and proxy_buffers 8 32k; resolves this immediately.


πŸ’‘ Summary

By combining Docker, Nginx, and Certbot on an AWS EC2 instance, you achieve:

  1. Low-cost multi-domain routing without multi-ALB overhead.
  2. Rapid SAN certificate expansion for dynamic tenant subdomains.
  3. High throughput with tuned proxy buffering and modern TLS protocols.

What strategies do you use for SSL automation on AWS? Let's discuss in the comments below!

Top comments (1)