Network
Netplan (bridge)
Edit /etc/netplan/50-cloud-init.yaml:
network:
bridges:
br0:
interfaces: [enp4s0]
dhcp4: true
version: 2
sudo netplan apply
MAC address changes after bridge setup — router may assign a new IP. Interface name (
enp4s0) may differ after hardware changes — check withip link show.
Caddy (reverse proxy)
Config: /etc/caddy/Caddyfile
{
servers {
protocols h1 h2
}
}
haos.alogins.net {
reverse_proxy http://192.168.1.141:8123 {
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
}
vi.alogins.net { reverse_proxy localhost:2283 }
doc.alogins.net { reverse_proxy localhost:11001 }
zb.alogins.net { reverse_proxy localhost:81 }
git.alogins.net { reverse_proxy localhost:3000 }
ai.alogins.net { reverse_proxy localhost:3125 }
ntfy.alogins.net { reverse_proxy localhost:8840 }
vw.alogins.net { reverse_proxy localhost:8041 }
tor.alogins.net { reverse_proxy localhost:8085 }
wiki.alogins.net {
reverse_proxy localhost:8083 {
header_up Host {http.request.host}
header_up X-Forwarded-Proto {scheme}
header_up X-Real-IP {remote_host}
}
}
nn.alogins.net { reverse_proxy localhost:5678 }
ds.alogins.net { reverse_proxy localhost:3974 }
openpi.alogins.net {
root * /home/alvis/tmp/files/pi05_droid
file_server browse
}
# Ad-hoc file sharing — explicit per-file path mapping, no directory listing
dl.alogins.net {
@chiefx8 path /chief-x8.zip
handle @chiefx8 {
root * /mnt/misc/qbittorrent/downloads
rewrite * "/Chief Architect Premier X8 18.1.1.4 Portable.zip"
file_server
}
respond 404
}
# VPN proxy (3X-UI)
vui3.alogins.net {
@xhttp { path /VLSpdG9k/xht* }
handle @xhttp {
reverse_proxy http://localhost:8445 {
flush_interval -1
header_up X-Real-IP {remote_host}
transport http {
read_timeout 0
write_timeout 0
dial_timeout 10s
}
}
}
reverse_proxy /gnYCNq4EbYukS5qtOe/* localhost:58959
respond 401
}
vui4.alogins.net { reverse_proxy localhost:58959 }
# oO recommendation system — path-based routing on one domain
o.alogins.net {
handle /api/* { reverse_proxy localhost:3078 }
handle /admin* { reverse_proxy localhost:3080 }
handle /mlflow* { reverse_proxy localhost:5000 } # MLflow (mlops profile)
handle /airflow* { reverse_proxy localhost:8080 } # Airflow (mlops profile)
handle { reverse_proxy localhost:3079 }
}
sudo systemctl enable --now caddy
HTTP/3 disabled globally — the top-level protocols h1 h2 option forces HTTP/1.1 and HTTP/2 only. OnlyOffice editor assets stall over HTTP/3 on distant connections (Caddy aborts with writing: timeout: no recent network activity), which breaks the Seafile → OnlyOffice flow. HTTP/2 handles the same payloads without issue.
Ad-hoc file sharing pattern
Use dl.alogins.net for temporary public downloads. Map each file to an explicit short path inside a handle block; default to respond 404 so the host does not leak structure. Caddy runs as the caddy user — make sure the file is world-readable (chmod o+r) or accessible to that user. Remove the handle block when no longer needed.
Hairpin NAT
The router does not support hairpin NAT — the server cannot reach its own public IP from within the LAN. Services that need to call back to a public domain from the host or from Docker containers must use local overrides.
Add to /etc/hosts on the host for any domain that needs to be reached locally:
# Loopback for self-hosted domains (hairpin NAT workaround)
127.0.0.1 docs.alogins.net
127.0.0.1 vw.alogins.net
For Docker containers, use extra_hosts in the compose file — routes to the host's Caddy via the Docker bridge gateway:
extra_hosts:
- "docs.alogins.net:host-gateway"
- "office.alogins.net:host-gateway"
Applied in both directions of the Seafile ⇄ OnlyOffice integration: the seafile container reaches office.alogins.net to download files after a save callback, and the seafile-onlyoffice container reaches docs.alogins.net to post callbacks. Without the extra_hosts the requests try the public IP and hang ~134s before timing out.
Port forwarding requirements:
- Ports 80 and 443 forwarded on the router (source and target, remote IP empty)
- Port 10051 forwarded for Zabbix active agent connections
TLS debugging
curl -v http://yourdomain.net/.well-known/acme-challenge/test
# Expect HTTP 200
Pi-hole
LAN-wide DNS ad-blocking runs on lizacer at 192.168.1.2. The router's DNS is set to that IP so every LAN client uses Pi-hole automatically. See Pi-hole for setup details.