Setting up a Dockerized NGINX Proxy on a VPS

⚠️ Warning: This setup is just a boilerplate that will allow you to request SSL certificates from letsencrypt. In order to serve websites, additional .conf files need to be created in ~/hosting/config/nginx/conf.d/.

This guide walks through the steps required to set up a containerized NGINX proxy server using Docker Compose on a Virtual Private Server (VPS). It includes basic HTTP-to-HTTPS redirection and prepares the server for integration with Let’s Encrypt for TLS certificate management.


Prerequisites

Make sure the following software is installed and functioning on your VPS:

  • Docker
  • Docker Compose
  • Basic shell tools (apt, mkdir, etc.)

Step 1: Install Docker Compose

Refer to the internal documentation at Docker Compose Setup for a comprehensive guide on how to install and configure Docker Compose on your VPS.


Step 2: Install Let’s Encrypt Client

Let’s Encrypt provides free TLS certificates. We use it to secure our web traffic over HTTPS.

Install the client with:

apt install letsencrypt

This client allows us to request and renew certificates. Later, it will interact with NGINX by placing challenge files in the .well-known directory that NGINX must serve to validate ownership of the domain.


Step 3: Prepare the Directory Structure

We’ll create a logical directory layout under ~/hosting that will house:

  • Configuration files (config)
  • Web content and ACME challenges (data)
  • NGINX-related settings and virtual host files
mkdir ~/hosting && \
mkdir -p ~/hosting/config/nginx/conf.d && \
mkdir -p ~/hosting/data/www/letsencrypt

This will create:

  • ~/hosting/config/nginx/conf.d: for custom virtual host configs
  • ~/hosting/data/www/letsencrypt: for Let’s Encrypt ACME challenge files

Step 4: Create the NGINX Configuration File

We’ll define a robust base configuration for NGINX that handles:

  • Basic performance tuning
  • Logging
  • Security headers
  • Gzip compression
  • SSL readiness (even if certs are not yet present)

The configuration also sets up a default server that listens on port 80. It:

  1. Serves the .well-known/acme-challenge/ directory to respond to Let’s Encrypt validation requests.
  2. Redirects all other traffic to HTTPS (which should be configured separately once certs are available).
echo '# nginx.conf — a robust all-round starting point

user  nginx;
worker_processes  auto;
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
    multi_accept        on;
}

http {
    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 2048;
    server_tokens       off;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] '
                      '"$request" $status $body_bytes_sent '
                      '"$http_referer" "$http_user_agent"';
    access_log  /var/log/nginx/access.log  main;

    gzip              on;
    gzip_disable      "msie6";
    gzip_vary         on;
    gzip_proxied      any;
    gzip_comp_level   5;
    gzip_buffers      16 8k;
    gzip_types        text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

    ssl_protocols             TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;

    client_max_body_size     16M;
    client_body_buffer_size 128k;
    client_header_buffer_size 1k;
    large_client_header_buffers 4 4k;

    add_header X-Frame-Options SAMEORIGIN;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";

    server {
        listen 80 default_server;
        server_name default;
        root /var/www/letsencrypt/;
        location /.well-known/acme-challenge/ { }
        location / {
            return 301 https://$host$request_uri;
        }
    }

    include /etc/nginx/conf.d/*.conf;
}' > ~/hosting/config/nginx.conf

Step 5: Create the docker-compose.yml File

This file defines our service stack. Currently, we only include an nginx service.

echo 'networks:
  default:
    name: extdev_network
services:
  nginx:
    container_name: nginx
    image: nginx:stable-alpine
    restart: unless-stopped
    volumes:
      - ./config/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./config/nginx/conf.d:/etc/nginx/conf.d:ro
      - ./data/www:/var/www
      - /etc/letsencrypt:/certs
    ports:
      - "80:80"
      - "443:443"' > ~/hosting/docker-compose.yml

Step 6: Start the NGINX Container

To start the NGINX proxy:

cd ~/hosting && docker compose up --build -d

This command builds and starts the container in detached mode. NGINX should now be listening on ports 80 and 443.


Next Steps

  • Ensure you create conf.d/default.conf to define your server blocks with support for IPv6 and correct root directories.
  • Setup HTTPS by configuring certbot and obtaining certificates for your domains.
  • Add additional reverse proxy rules for services or applications behind NGINX.