Skip to content

Secure Self-Hosting: Docker Compose and Nginx Proxy Manager on Raspberry Pi

Running self-hosted services (like a private cloud, password manager, or ad-blocker) on a single Raspberry Pi is highly efficient. However, as you add more services, you run into port conflicts (e.g. multiple services wanting port 80/443) and the complexity of managing SSL certificates for secure HTTPS access.

This guide shows you how to resolve port conflicts and configure automatic HTTPS routing using Docker Compose and Nginx Proxy Manager.


1. Architecture Overview

Rather than exposing multiple custom ports (like http://192.168.1.100:8080 or http://192.168.1.100:3000) to your local network, a Reverse Proxy intercepts all traffic on standard HTTP/HTTPS ports (80/443) and routes requests to the correct container based on the incoming domain name.

                  Local Network Client (Browser)
                        (e.g., vault.local)
                  Nginx Proxy Manager (Port 80/443)
            ┌───────────────────┴───────────────────┐
            │ (Proxy Pass)                          │ (Proxy Pass)
            ▼                                       ▼
    Vaultwarden (Port 8080)                  Pi-hole Admin (Port 8081)

2. Prerequisites

Ensure you have Docker and Docker Compose installed on your Raspberry Pi:

1
2
3
4
5
6
# Install Docker
curl -sSL https://get.docker.com | sh
sudo usermod -aG docker $USER

# Install Docker Compose dependencies
sudo apt install -y python3-pip libffi-dev

Note: Log out and log back in to apply the group membership.


3. The Unified Docker Compose Configuration

Create a dedicated directory for your server setup and create a docker-compose.yml file:

mkdir -p ~/selfhost && cd ~/selfhost
nano docker-compose.yml

Paste the following configurations. This starts Nginx Proxy Manager (handling SSL and routing), Vaultwarden (lightweight Bitwarden password manager server), and Pi-hole (DNS ad-blocker):

version: '3.8'

networks:
  proxy-tier:
    name: proxy-tier

services:
  # Nginx Proxy Manager (Reverse Proxy)
  npm:
    image: 'jc21/nginx-proxy-manager:latest'
    container_name: npm
    restart: unless-stopped
    ports:
      - '80:80'   # Public HTTP Port
      - '443:443' # Public HTTPS Port
      - '81:81'   # Admin Web Port
    volumes:
      - ./npm/data:/data
      - ./npm/letsencrypt:/etc/letsencrypt
    networks:
      - proxy-tier

  # Vaultwarden (Password Manager)
  vaultwarden:
    image: 'vaultwarden/server:latest'
    container_name: vaultwarden
    restart: unless-stopped
    environment:
      - WEBSOCKET_ENABLED=true
    volumes:
      - ./vaultwarden:/data
    networks:
      - proxy-tier
    # Note: No ports are exposed to the host! Only the proxy can access it.

  # Pi-hole (Ad-blocker & Local DNS Server)
  pihole:
    image: 'pihole/pihole:latest'
    container_name: pihole
    restart: unless-stopped
    ports:
      - '53:53/tcp' # DNS Ports must be host-exposed
      - '53:53/udp'
    environment:
      - TZ=UTC
      - WEBPASSWORD=ChooseSecureAdminPassword
    volumes:
      - ./pihole/etc-pihole:/etc/pihole
      - ./pihole/etc-dnsmasq.d:/etc/dnsmasq.d
    networks:
      - proxy-tier

Launching the Stack

Start all services in detached background mode:

docker compose up -d

4. Configuring Nginx Proxy Manager

1. Log in to the Admin Panel

Open your browser and navigate to http://<RASPBERRY_PI_IP>:81. - Default Email: admin@example.com - Default Password: changeme (You will be prompted to change these details immediately upon login).

2. Add a Proxy Host

Let's route vault.local to the Vaultwarden container: 1. Click Hosts $\rightarrow$ Proxy Hosts $\rightarrow$ Add Proxy Host. 2. Domain Names: Enter vault.local (or a domain name you own). 3. Scheme: Select http. 4. Forward Hostname / IP: Enter the container name: vaultwarden. (Because they share the same Docker network, Docker's internal DNS resolves this name). 5. Forward Port: Enter 80 (Vaultwarden's internal listening port). 6. Enable Block Common Exploits and Websockets Support.

1
2
3
4
    Domain Names: vault.local
    Scheme: http
    Forward Hostname: vaultwarden
    Forward Port: 80

3. Configure SSL (HTTPS)

Under the SSL tab: 1. Select Request a new SSL Certificate from the SSL Certificate dropdown. 2. Check Force SSL and HTTP/2 Support. 3. Accept the Let's Encrypt Terms of Service and click Save.

Nginx Proxy Manager will contact Let's Encrypt, retrieve a valid SSL certificate, configure Nginx, and reload the settings automatically.


5. Local Network Routing (DNS)

To access vault.local from other devices on your home network, those devices must know that vault.local points to your Raspberry Pi's IP address.

Since you are running Pi-hole, this is extremely easy to configure: 1. Access the Pi-hole admin interface at http://<RASPBERRY_PI_IP>:80/admin (or route it through NPM first). 2. Go to Local DNS $\rightarrow$ DNS Records. 3. Domain: Enter vault.local. 4. IP Address: Enter your Raspberry Pi's local IP address (e.g. 192.168.1.100). 5. Click Add.

Now, configure your router or local devices to use your Raspberry Pi's IP address as their primary DNS server.