Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🚀 Multi-Site Cloud Hosting Environment (Docker + Nginx Reverse Proxy)

An automated, hardened, and containerized multi-site web hosting environment deployed on Hetzner Cloud. This infrastructure serves isolated static and dynamic applications behind a single Nginx reverse proxy utilizing wildcard subdomain routing via Cloudflare DNS.


🌐 1. Live Validation URLs & Visual Proof

The environment is currently deployed, hardened, and fully accessible from the public internet:

📸 Live Browser Validation

All three distinct web applications operating concurrently through a single ingress gateway:

Live Sites Proof


☁️ 2. Cloud Infrastructure & DNS Routing

Hetzner Cloud Provisioning

The underlying virtual machine is hosted on an Ubuntu 24.04 LTS instance utilizing the cost-optimized CX23 x86 architecture (2 vCPU, 4 GB RAM, 40 GB NVMe SSD) located in the eu-central (Helsinki) availability zone.

Hetzner CX23 Specs Hetzner Hostname Verification

Cloudflare Wildcard DNS Configuration

Subdomain traffic is routed via Cloudflare DNS. Three explicit A records map the subdomains directly to the public IPv4 address 157.180.42.147. Proxying is intentionally set to DNS Only (Grey Cloud) to ensure pure, uncached HTTP requests reach the Docker reverse proxy directly during resilience evaluation.

Cloudflare DNS Records


🧱 3. Architecture & Network Isolation

                                +-------------------+
                                |   Public Internet |
                                +---------+---------+
                                          |
                                          | HTTP (Port 80)
                                          v
                              +-----------+-----------+
                              |    UFW Firewall       |
                              +-----------+-----------+
                                          |
                                          | Allow 80/tcp
                                          v
+-----------------------------------------+-----------------------------------------+
| HETZNER CLOUD INSTANCE (157.180.42.147)                                           |
|                                                                                   |
|  +-----------------------------------------------------------------------------+  |
|  | DOCKER BRIDGE NETWORK: hosting-net (Isolated)                               |  |
|  |                                                                             |  |
|  |  +---------------------+                                                    |  |
|  |  |    nginx-proxy      |  <-- Single Public Ingress (0.0.0.0:80 -> 80)      |  |
|  |  +----------+----------+                                                    |  |
|  |             |                                                               |  |
|  |             +-----------------------+-----------------------+               |  |
|  |             | Host: site1           | Host: site2           | Host: php     |  |
|  |             v                       v                       v               |  |
|  |  +----------+----------+ +----------+----------+ +----------+----------+    |  |
|  |  |   site1-container   | |   site2-container   | |    php-container    |    |  |
|  |  |   (nginx:alpine)    | |   (nginx:alpine)    | |  (php:8.2-apache)   |    |  |
|  |  +---------------------+ +---------------------+ +---------------------+    |  |
|  |    Internal Port 80        Internal Port 80        Internal Port 80         |  |
|  +-----------------------------------------------------------------------------+  |
+-----------------------------------------------------------------------------------+

Key Architectural Specifications:

  • Single Public Ingress (nginx-proxy): Nginx Alpine listening strictly on public port 80:80. Acts as the sole entry point for all incoming HTTP traffic.
  • Zero Exposed Backend Ports: Backend containers (site1, site2, php-app) do not publish ports to the host interface. All inter-service communication occurs strictly within the isolated Docker bridge network (hosting-net).
  • Decoupled Routing: Nginx routes requests dynamically based on HTTP Host headers, ensuring complete network isolation and preventing direct external access to internal microservices.

Docker Compose Orchestration


🔒 4. Security & System Hardening

The instance is secured following industry-standard Linux and Cloud Native hardening guidelines:

  1. Firewall (UFW): Configured with a strict Default Deny Incoming policy. Exclusively ports 22/tcp (OpenSSH) and 80/tcp (HTTP) are allowed from the outside world.
  2. SSH Authentication: Password-based login is permanently disabled in /etc/ssh/sshd_config. Access is restricted strictly to cryptographic Ed25519 SSH keys.
  3. Secret & Repository Hygiene: Zero hardcoded credentials, .env files, or private keys are committed to the version control system.

UFW Firewall Status Verbose


🧪 5. Resilience & Failure Recovery Evaluation

To satisfy high-availability requirements, all containers are orchestrated with restart: unless-stopped policies. The environment is engineered to survive process crashes, graceful service terminations, and unexpected host reboots without manual intervention.

Test 1: Process Crash Recovery (SIGTERM on PID 1)

Simulating an unexpected application crash by terminating the primary Nginx process inside the container:

docker exec site2-container kill -15 1
docker ps

Result: The Docker daemon detects PID 1 termination and immediately revives the container within 1 second (Up 1 second).

Crash Recovery Test - Kill

Test 2: Graceful Service Termination Recovery

Simulating an internal service shutdown using native application stop signals:

docker exec site2-container nginx -s stop
docker ps

Result: Upon graceful exit of the Nginx master process, the container is instantly resurrected by the orchestration engine (Up 2 seconds).

Crash Recovery Test - Stop

Test 3: Complete Host Reboot Survival

Simulating a hard server reboot or power-cycle event:

sudo reboot
# Wait 30 seconds -> Reconnect via SSH -> Check container state
docker ps

Result: Upon OS boot, Docker daemon automatically restores the entire hosting-net bridge network and starts all 4 containers concurrently (Up About a minute).

Reboot Survival Test


🛠 6. Step-by-Step Deployment Guide (Reproducible Setup)

To deploy this environment from scratch on a clean Ubuntu 24.04 LTS instance:

Step 1: System Update & Hardening

sudo apt update
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw --force enable
sudo ufw status verbose

Step 2: Install Official Docker Engine

curl -fsSL [https://get.docker.com](https://get.docker.com) -o get-docker.sh
sudo sh get-docker.sh
docker --version
docker compose version

Docker Installation Proof

Step 3: Clone Repository & Launch Infrastructure

git clone [https://github.com/leoleiden/multi-site-hosting.git](https://github.com/leoleiden/multi-site-hosting.git)
cd multi-site-hosting
docker compose up -d
docker compose ps

👨‍💻 Author & Engineering Profile

Leonid Lachman
DevOps & Data Engineer

About

Automated multi-site web hosting environment using Docker Compose, Nginx reverse proxy, and UFW security hardening.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages