|
|
||
|---|---|---|
| core-proxy | ||
| firewall | ||
| sites | ||
| .gitignore | ||
| LICENSE | ||
| README.md | ||
| setup.sh | ||
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- Let’s Encrypt HTTP-01 certificates
- Traefik dashboard at
https://traefik.example.com, protected by Authelia - Authelia at
https://auth.example.comfor login
-
Modular site stacks under
sites/:static-site: static file hosting athttps://example.comwordpress-site: WordPress athttps://example.com/wpforgejo: Forgejo (Git hosting) + CI runner athttps://git.example.comnextcloud: Nextcloud cloud storage athttps://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
- Default-deny firewall template (
-
Host-friendly data layout
- Each site’s data lives in bind-mounted directories under
sites/ - Easy to back up, rsync, or inspect without entering containers
- Each site’s data lives in bind-mounted directories under
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, you’ll need DNS A (and/or AAAA) records pointing to your server:
example.com→ server IPauth.example.com→ server IPtraefik.example.com→ server IPgit.example.com→ server IPcloud.example.com→ server IP
Quick Start
-
Clone the repo
git clone https://example.com/your/hosting-framework.git cd hosting-framework -
Create shared Docker network
docker network create traefik_proxy -
Prepare Traefik ACME storage
cd core-proxy mkdir -p traefik/dynamic touch traefik/acme.json chmod 600 traefik/acme.json -
Edit core config
-
core-proxy/docker-compose.yml- Change
admin@example.comto a real email - Adjust domains in Traefik labels for your use case
- Change
-
core-proxy/traefik/dynamic/authelia.yml- Set
auth.example.com(or equivalent)
- Set
-
core-proxy/authelia/configuration.yml- Change
example.com, secrets, etc.
- Change
-
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:.
-
-
-
Start the core stack
cd core-proxy docker compose up -d -
Configure DNS
Create DNS records pointing your domains to the server’s public IP. Let’s Encrypt will fail if DNS is wrong or not propagated.
-
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 -
Test
https://traefik.<your-domain>→ Authelia login → Traefik dashboardhttps://auth.<your-domain>→ Authelia portalhttps://example.com→ static sitehttps://example.com/wp→ WordPress installerhttps://git.<your-domain>→ Authelia login → Forgejo setuphttps://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 Traefikacme.json)
Minimal coupling
The only shared assumptions between stacks:
- A Docker network named
traefik_proxy - Authelia’s forward-auth middleware named
authelia-auth@file - Traefik’s Let’s 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=weborwebsecure -
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
- Define middleware:
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/andwp/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 traefikor 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:
- Create
sites/<new-site>/docker-compose.yml. - Attach it to
traefik_proxy. - Add Traefik labels for hostnames, HTTPS, redirect, and optionally Authelia.
- Bind volumes for data so they live on the host.
docker compose up -din that directory.- 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.