█████████ ███ ██████████ ████████ ██████████ ███▒▒▒▒▒███ ▒▒▒ ▒▒███▒▒▒▒███ ███▒▒▒▒███ ▒███▒▒▒▒███ ▒███ ▒▒▒ ████ ▒███ ▒▒███ ▒███ ▒▒▒ ▒▒▒ ███ ▒▒█████████ ▒▒███ ▒███ ▒███ ▒█████████ ███ ▒▒▒▒▒▒▒▒███ ▒███ ▒███ ▒███ ▒███▒▒▒▒███ ███ ███ ▒███ ▒███ ▒███ ███ ▒███ ▒███ ███ ▒▒█████████ █████ ██████████ ▒▒████████ ██ ███ ▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒ ▒▒ ▒▒▒

☠ Cyber Realm ☠

Underground Archive // Access Granted

From Simple Pi Server to Reverse Proxy Architecture

A practical journey from “just running containers” to understanding infrastructure design.


The Goal

I rebuilt my Raspberry Pi 4 (8GB, NVMe boot) home server from scratch.
The original goal was simple:

  • Run Docker
  • Host a few services
  • Keep it secure
  • Learn along the way

What started as a basic container setup turned into a deeper understanding of reverse proxies, networking, and reducing attack surface.


Phase 1: Clean Base System

Fresh install of Raspberry Pi OS Lite.

Security baseline:

  • SSH key-only authentication
  • Disabled password login
  • UFW enabled (default deny incoming)
  • Custom SSH port

This created a minimal and hardened foundation before adding any services.


Phase 2: Dockerized Services

I added:

  • Homepage (dashboard)
  • File Browser (file management)

Initial Docker setup exposed ports directly:

Service Port


Homepage 3000 File Browser 8080

Access looked like:

http://rpi-server.local:3000
http://rpi-server.local:8080

It worked — but something felt off.


The Realization: Too Many Exposed Ports

Running:

docker ps

Showed:

homepage    0.0.0.0:3000->3000/tcp
filebrowser 0.0.0.0:8080->80/tcp

That meant:

  • Multiple services were publicly reachable
  • UFW had multiple allow rules
  • Every service was an entry point

Even though this was “just a LAN server,” the attack surface was larger than necessary.

That question — “Can I close off those exposed ports?” — led directly to learning about reverse proxies.


Phase 3: Introducing Nginx (Reverse Proxy)

Instead of exposing every service, I added an Nginx container.

New architecture:

Client
   ↓
Port 80 (Nginx)
   ↓
Internal Docker Network
   ↓
Homepage / Filebrowser

Only Nginx is exposed externally.

All other containers are internal-only.


Docker Compose (Final Structure)

Reverse Proxy (Nginx)

nginx:
  image: nginx:alpine
  container_name: reverse-proxy
  ports:
    - "80:80"
  volumes:
    - ./config/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
  restart: unless-stopped

File Browser (Internal Only)

filebrowser:
  image: filebrowser/filebrowser
  container_name: filebrowser
  expose:
    - "80"
  volumes:
    - /srv:/srv
    - ./config/filebrowser:/config
    - ./config/filebrowser-db:/database
  restart: unless-stopped

Homepage (Internal Only)

homepage:
  image: ghcr.io/gethomepage/homepage:latest
  container_name: homepage
  expose:
    - "3000"
  environment:
    HOMEPAGE_ALLOWED_HOSTS: rpi-server.local
  volumes:
    - ./config/homepage:/app/config
  restart: unless-stopped

Key difference:

  • ports: exposes a service to the host.
  • expose: makes it available only inside Docker.

Nginx Routing Configuration

events {}

http {
  server {
    listen 80;
    server_name rpi-server.local;

    location / {
      proxy_pass http://homepage:3000;
    }

    location /files/ {
      proxy_pass http://filebrowser:80/;
    }
  }
}

Access now looks like:

http://rpi-server.local
http://rpi-server.local/files

No more port numbers.


Firewall Cleanup

Before:

ALLOW 3000/tcp
ALLOW 8080/tcp
ALLOW 80/tcp

After:

ALLOW 80/tcp (LAN only)
ALLOW SSH

Attack surface significantly reduced.


What Actually Changed

Before:

LAN → Homepage (3000)
LAN → Filebrowser (8080)

After:

LAN → Nginx (80)
        ↓
    Internal Docker Network
        ↓
   Services (not directly reachable)

Only one exposed entry point.

This mirrors real-world infrastructure design.


Lessons Learned

  1. Reverse proxies centralize control.
  2. Docker ports vs expose matters for security.
  3. Host header validation prevents misuse.
  4. VPNs can affect local hostname routing.
  5. Path-based proxying may require base URL adjustments.

Why This Matters

Understanding reverse proxies teaches:

  • Infrastructure layering
  • Trust boundaries
  • Attack surface reduction
  • Header handling
  • Real-world architecture patterns

This wasn’t just “installing Nginx.”

It was learning why modern systems are built the way they are.


Final Architecture

LAN
  ↓
Port 80 → Nginx
            ↓
     Docker Internal Network
            ↓
   Homepage + Filebrowser

Minimal. Controlled. Intentional.


Next Steps

  • Add HTTPS internally
  • Implement rate limiting
  • Add logging analysis
  • Introduce additional services behind the proxy

This started as a simple Pi server.
It evolved into understanding infrastructure design.

And that’s the real win.