Software stack for virtual host serving based on Docker + Traefik + Authelia.
Go to file
Wesley R. Elsberry c609104708 Added more files/scripts for Wireguard usage, modified others. 2025-11-20 09:01:42 -05:00
core-proxy Initial files commit 2025-11-19 13:53:03 -05:00
firewall Added more files/scripts for Wireguard usage, modified others. 2025-11-20 09:01:42 -05:00
sites Added portected site and directory examples 2025-11-19 14:13:52 -05:00
wireguard Added more files/scripts for Wireguard usage, modified others. 2025-11-20 09:01:42 -05:00
.gitignore Initial files commit 2025-11-19 13:53:03 -05:00
LICENSE Initial commit 2025-11-19 13:22:42 -05:00
README.md Edited README.md to add Wireguard section 2025-11-20 08:55:19 -05:00
setup.sh Initial files commit 2025-11-19 13:53:03 -05:00

README.md

VHostLoom

A Docker Hosting Framework: Traefik + Authelia + Modular Sites

This repository provides a reusable framework for running multiple virtual-hosted web services on a single server using:

  • Traefik as a reverse proxy and TLS terminator
  • Authelia as an SSO / authentication gateway
  • Docker Compose for container orchestration
  • Modular per-site stacks with bind-mounted volumes

The design goals are:

Add or change sites without constantly editing core proxy config
and keep application data available natively on the host filesystem.

Under the view that hacking is inevitable, it seemed to me that making it so a hacker has to hack each container separately makes the system less brittle and less exposed to threats like ransomware. With modest backup discipline, this system should permit porting or getting back up expeditiously.


Features

  • Core proxy stack (core-proxy/) with Traefik and Authelia

    • Lets Encrypt HTTP-01 certificates
    • Traefik dashboard at https://traefik.example.com, protected by Authelia
    • Authelia at https://auth.example.com for login
  • Modular site stacks under sites/:

    • static-site: static file hosting at https://example.com
    • wordpress-site: WordPress at https://example.com/wp
    • forgejo: Forgejo (Git hosting) + CI runner at https://git.example.com
    • nextcloud: Nextcloud cloud storage at https://cloud.example.com
  • Security model

    • Default-deny firewall template (firewall/nftables.conf.example)
    • Only 80/443 exposed publicly (and optionally a few others)
    • Authelia used to put an extra auth layer in front of sensitive apps
    • Easy integration with ZeroTier or other VPNs for “VPN-only” services
  • Host-friendly data layout

    • Each sites data lives in bind-mounted directories under sites/
    • Easy to back up, rsync, or inspect without entering containers

Prerequisites

  • Linux host (e.g., Ubuntu Server 22.04+)
  • Docker and Docker Compose plugin installed
  • A public IP address (static or effectively static)
  • Control over DNS records for your domains

For HTTPS, youll need DNS A (and/or AAAA) records pointing to your server:

  • example.com → server IP
  • auth.example.com → server IP
  • traefik.example.com → server IP
  • git.example.com → server IP
  • cloud.example.com → server IP

Quick Start

  1. Clone the repo

    git clone https://example.com/your/hosting-framework.git
    cd hosting-framework
    
    
    
  2. Create shared Docker network

    docker network create traefik_proxy
    
  3. Prepare Traefik ACME storage

    cd core-proxy
    mkdir -p traefik/dynamic
    touch traefik/acme.json
    chmod 600 traefik/acme.json
    
  4. Edit core config

    • core-proxy/docker-compose.yml

      • Change admin@example.com to a real email
      • Adjust domains in Traefik labels for your use case
    • core-proxy/traefik/dynamic/authelia.yml

      • Set auth.example.com (or equivalent)
    • core-proxy/authelia/configuration.yml

      • Change example.com, secrets, etc.
    • core-proxy/authelia/users_database.yml

      • Generate a password hash:

        docker run --rm authelia/authelia:latest authelia hash-password 'yourpassword'
        

        Paste the hash into password:.

  5. Start the core stack

    cd core-proxy
    docker compose up -d
    
  6. Configure DNS

    Create DNS records pointing your domains to the servers public IP. Lets Encrypt will fail if DNS is wrong or not propagated.

  7. Bring up example sites

    # Static site
    cd ../sites/static-site
    docker compose up -d
    
    # WordPress
    cd ../wordpress-site
    docker compose up -d
    
    # Forgejo
    cd ../forgejo
    docker compose up -d
    
    # Nextcloud
    cd ../nextcloud
    docker compose up -d
    
  8. Test

    • https://traefik.<your-domain> → Authelia login → Traefik dashboard
    • https://auth.<your-domain> → Authelia portal
    • https://example.com → static site
    • https://example.com/wp → WordPress installer
    • https://git.<your-domain> → Authelia login → Forgejo setup
    • https://cloud.<your-domain> → Authelia login → Nextcloud setup

Rationale & Design

Separation of concerns

  • Core proxy (Traefik + Authelia) is stable and rarely changed.
  • Sites live in separate directories with their own docker-compose.yml.
  • Firewall is independent of Docker and enforces network boundaries.

This makes it easy to:

  • Add new virtual hosts (just add a new sites/<name>/docker-compose.yml)
  • Share the framework without embedding secrets
  • Back up only what matters (sites/**, Authelia DB, maybe Traefik acme.json)

Minimal coupling

The only shared assumptions between stacks:

  • A Docker network named traefik_proxy
  • Authelias forward-auth middleware named authelia-auth@file
  • Traefiks Lets Encrypt resolver named letsencrypt

Everything else is per-site.


Configuration Details

Traefik labels

Each service defines its routing behavior entirely via labels:

  • Match host and path: traefik.http.routers.<name>.rule=Host(example.com) && PathPrefix(/wp)

  • Bind to entrypoint: ...entrypoints=web or websecure

  • Enable HTTPS / certificates: ...tls.certresolver=letsencrypt

  • HTTP→HTTPS redirect using a middleware:

    • Define middleware: traefik.http.middlewares.foo-https-redirect.redirectscheme.scheme=https
    • Attach it to an HTTP router: traefik.http.routers.foo-http.middlewares=foo-https-redirect

Authelia protection

To require login via Authelia before an app:

labels:
  - "traefik.http.routers.<name>-https.middlewares=authelia-auth@file"

Remove or comment this label to make a site public.

Authelia users

Defined in core-proxy/authelia/users_database.yml:

users:
  admin:
    displayname: "Admin User"
    email: "admin@example.com"
    groups: [admins]
    password: "<argon2id hash>"

You can use groups and more complex access_control rules if desired; the default config in this repo simply treats any authenticated user as allowed.


Security & Firewall

The firewall/nftables.conf.example file contains a default-deny firewall with:

  • Loopback and established connections allowed
  • ICMP allowed (optional but recommended)
  • SSH allowed only over a VPN interface (e.g., ZeroTier)
  • 80/443 open for Traefik
  • Example ports restricted to the VPN interface

You can adapt it and enable with:

sudo cp firewall/nftables.conf.example /etc/nftables.conf
sudo nft -f /etc/nftables.conf
sudo systemctl enable nftables

Always test SSH access before locking down too far.


Maintenance

Updating containers

For each stack:

cd core-proxy
docker compose pull
docker compose up -d

cd ../sites/<site-name>
docker compose pull
docker compose up -d

Backups

At minimum, back up:

  • core-proxy/traefik/acme.json (certificates)

  • core-proxy/authelia/ (configuration + DB)

  • sites/**/ data directories:

    • sites/static-site/html/
    • sites/wordpress-site/db/ and wp/
    • sites/forgejo/data/, db/, runner/
    • sites/nextcloud/nextcloud/, db/, redis/

Use rsync, borg, restic, or your favorite solution.

Logs

  • Traefik logs: stdout (use docker logs traefik or a log collector)
  • Authelia logs: stdout + notification.log (if configured)
  • Site logs: as exposed via each container

Extending the Framework

To add a new site:

  1. Create sites/<new-site>/docker-compose.yml.
  2. Attach it to traefik_proxy.
  3. Add Traefik labels for hostnames, HTTPS, redirect, and optionally Authelia.
  4. Bind volumes for data so they live on the host.
  5. docker compose up -d in that directory.
  6. Add DNS records for the new hostname(s).

You never need to edit the core proxy configuration for new sites.


License

This software is licensed under the MIT license.

Disclaimer

Beyond the MIT license statement, this software was produced by iterative prompting of OpenAI's GPT 5.1 LLM.

Notes

For myself, I only want the core-proxy items on the boot drive. The 'sites' directory I am sym-linking from a mount that has much more disk space.

Also, Docker containers can be large. Consider sym-linking the Docker image directory to a larger disk.

Secure Private Access (WireGuard Module)

Some services in VHostLoom are not public-facing by design:

  • Stable Diffusion interfaces
  • Llamafile demos & research endpoints
  • Ollama/vLLM
  • Internal dashboards
  • Forgejo SSH
  • Anything experimental or non-web

To protect these services, VHostLoom supports an optional WireGuard VPN module that restricts private-service ports so they are reachable only from authenticated VPN clients, and never from the public Internet.

WireGuard may be used instead of, or alongside, ZeroTier.


Why WireGuard?

WireGuard is:

  • extremely fast (kernel-level cryptography)
  • small and security-audited
  • widely supported across platforms
  • perfect for “private access only” services

VHostLooms WireGuard module:

  • Exposes WireGuard only on WAN (udp/51820)
  • Creates a private VPN subnet (10.20.0.0/24)
  • Restricts critical ports to the WireGuard interface
  • Leaves Traefik-managed public services untouched

Installing WireGuard

Install on the server:

sudo apt install wireguard wireguard-tools

Copy in the example config:

sudo mkdir -p /etc/wireguard
sudo cp wireguard/wg0.conf.example /etc/wireguard/wg0.conf
sudo chmod 600 /etc/wireguard/wg0.conf

Generate server keypair:

wg genkey | tee server.key | wg pubkey > server.pub

Add the server private key to:

PrivateKey = <SERVER_PRIVATE_KEY>

Starting WireGuard

sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0

Verify:

ip addr show wg0
wg show

Firewall Configuration

Choose one:

  • firewall/nftables-wireguard.conf.example — basic WireGuard + Traefik + private-services setup
  • firewall/nftables-wireguard-zt.conf.examplecombined WireGuard + ZeroTier rules

Install a firewall:

sudo cp firewall/nftables-wireguard.conf.example /etc/nftables.conf
sudo nft -f /etc/nftables.conf
sudo systemctl enable nftables

This:

  • Allows public web (80/443)
  • Accepts WireGuard on WAN (UDP 51820)
  • Allows private services (only via wg0)
  • Default-denies everything else

Adding a New WireGuard Client

Use the helper script:

wireguard/gen-wg-peer.sh clientname

It will generate:

  • clientname.key
  • clientname.pub
  • clientname.conf (the client config)

Add the peer entry to /etc/wireguard/wg0.conf automatically.

Then send the generated .conf file to your device.


Client Template

Clients use a simple config:

[Interface]
PrivateKey = <CLIENT_PRIVATE_KEY>
Address = 10.20.0.X/32
DNS = 1.1.1.1

[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
Endpoint = <PUBLIC_IP>:51820
AllowedIPs = 10.20.0.0/24
PersistentKeepalive = 25

Import into:

  • WireGuard app (iOS/Android)
  • wg-quick
  • Desktop clients

Coexisting with ZeroTier

If you want both overlay networks:

  • ZeroTier mesh connections
  • WireGuard direct VPN
  • Shared access control for private ports

Use:

firewall/nftables-wireguard-zt.conf.example

It grants private-port access to either:

  • WireGuard (wg0)
  • ZeroTier (zt*)

You can also restrict different services to different VPNs.


Summary

WireGuard gives VHostLoom:

  • Strong isolation of private services
  • Minimal attack surface
  • Predictable firewalling
  • Fast, encrypted access

It integrates fully with the projects model:

  • Traefik/Authelia for public-facing authenticated web
  • WireGuard (and/or ZeroTier) for non-web private services