The fastest, leanest VPN protocol in existence. Kernel-level performance, handles hundreds of Mbps on a single core.
+--------+ UDP/51820 +--------+
| Client |<================>| VPS |
| Phone | Encrypted Tunnel |WireGrd |
| Laptop | +--------+
+--------+ |
+----+----+
|10.0.0.0 |
| Subnet |
services:
wireguard:
image: linuxserver/wireguard
container_name: wireguard
cap_add: [NET_ADMIN, SYS_MODULE]
environment:
PUID: 1000
PGID: 1000
TZ: UTC
SERVERURL: auto
SERVERPORT: 51820
PEERS: phone,laptop,desktop
PEERDNS: auto
INTERNAL_SUBNET: 10.13.13.0
volumes:
- ./config:/config
- /lib/modules:/lib/modules
ports:
- 51820:51820/udp
sysctls:
net.ipv4.conf.all.src_valid_mark: 1
restart: unless-stopped
# Not proxied (UDP protocol). Serve config download page:
wireguard.example.com {
reverse_proxy localhost:51821
}
Backup /config dir: tar czf wg-backup.tar.gz ./config
wg show
Self-hosted Tailscale control server. Zero-config mesh VPN, works behind NAT, handles key exchange, ACLs, and DNS.
+--------+ DERP/HTTPS +----------+ WireGuard +--------+
| Device |<===============>| Headscale|<=============>| Device |
| Phone | | Control | | Laptop |
+--------+ | Server | +--------+
+----------+
services:
headscale:
image: headscale/headscale:latest
container_name: headscale
volumes:
- ./config:/etc/headscale
- ./data:/var/lib/headscale
ports:
- 8080:8080
command: headscale serve
restart: unless-stopped
headscale-ui:
image: ghcr.io/gurucomputing/headscale-ui:latest
container_name: headscale-ui
ports:
- 8443:80
restart: unless-stopped
headscale.example.com {
reverse_proxy headscale:8080
}
headscale-ui.example.com {
reverse_proxy headscale-ui:80
}
Backup config/ and data/ directories (includes SQLite DB)
docker exec headscale headscale nodes list
Expose local services without opening ports. Free Cloudflare proxy with DDoS protection included.
Internet --> Cloudflare Edge --> Tunnel --> VPS Services
|
+----------+----------+
| cloudflared |
| :8080 :3000 :5000 |
services:
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
command: tunnel --no-autoupdate run
environment:
TUNNEL_TOKEN: your-token-here
restart: unless-stopped
# Not needed - Cloudflare IS the reverse proxy # Configure in Cloudflare dashboard
Token stored in Cloudflare dashboard. Stateless via token.
docker logs cloudflared --tail 5
Network-wide DNS ad blocker. Block ads, trackers, and malware for every device. Beautiful web dashboard.
Devices --> Pi-hole DNS (:53) --> Upstream DNS
| |
| Block List
| Check
v v
Allowed Blocked
(resolve) (0.0.0.0)
services:
pihole:
image: pihole/pihole:latest
container_name: pihole
environment:
TZ: UTC
WEBPASSWORD: changeme
DNS1: 127.0.0.1#5335
DNS2: "no"
volumes:
- ./etc-pihole:/etc/pihole
- ./etc-dnsmasq.d:/etc/dnsmasq.d
ports:
- "53:53/tcp"
- "53:53/udp"
- "8080:80"
cap_add: [NET_ADMIN]
restart: unless-stopped
pihole.example.com {
reverse_proxy pihole:80
}
Backup etc-pihole/ and etc-dnsmasq.d/ directories
dig @localhost example.com +short
Pi-hole alternative with slicker UI, built-in HTTPS, and DNS-over-HTTPS/TLS support. Modern ad-blocking at DNS level.
Devices --> AdGuard DNS (:53/:853) --> Upstream DoH/DoT
|
Block Lists
+ Safe Browsing
+ Parental Ctrl
services:
adguard:
image: adguard/adguardhome:latest
container_name: adguard
volumes:
- ./work:/opt/adguardhome/work
- ./conf:/opt/adguardhome/conf
ports:
- "53:53/tcp"
- "53:53/udp"
- "853:853/tcp"
- "3000:3000"
- "8080:80"
restart: unless-stopped
adguard.example.com {
reverse_proxy adguard:80
}
Backup work/ and conf/ directories
curl -s localhost:3000/control/status
Validating, recursive, caching DNS resolver with DNSSEC. Run your own DNS instead of trusting Google/Cloudflare.
Pi-hole --> Unbound (:5335) --> Root DNS Servers
|
Recursive
Resolution
+ DNSSEC
Validation
services:
unbound:
image: mvance/unbound:latest
container_name: unbound
volumes:
- ./unbound.conf:/opt/unbound/etc/unbound/unbound.conf
ports:
- "5335:5335/tcp"
- "5335:5335/udp"
restart: unless-stopped
# Internal DNS only - not exposed to internet
Backup unbound.conf. Config is minimal.
dig @localhost -p 5335 example.com +dnssec +short
Self-hosted Bitwarden-compatible password manager. Single Rust binary, works with all official Bitwarden apps and extensions.
+--------+ HTTPS +-----------+ +-------+ |Browser |<===========>|Vaultwarden|<===>| SQLite | |App/CLI | | :8080 | | DB | +--------+ +-----------+ +-------+
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
environment:
DOMAIN: https://vw.example.com
SIGNUPS_ALLOWED: "false"
WEBSOCKET_ENABLED: "true"
ADMIN_TOKEN: your-admin-token
volumes:
- ./vw-data:/data
ports:
- "8080:80"
restart: unless-stopped
vw.example.com {
reverse_proxy vaultwarden:80
}
Backup vw-data/ dir (SQLite DB + attachments). CRITICAL.
curl -s http://localhost:8080/alive
Simple HTTP-based push notification service. Send alerts to phone/desktop from scripts, cron jobs, or any HTTP request.
Script/Cron --> ntfy (:80) --> Phone App
|
WebSocket
+ WebPush
+ Firebase
services:
ntfy:
image: binwiederhier/ntfy:latest
container_name: ntfy
command: serve
environment:
NTFY_BASE_URL: https://ntfy.example.com
NTFY_CACHE_FILE: /var/cache/ntfy/cache.db
NTFY_AUTH_FILE: /var/lib/ntfy/user.db
NTFY_AUTH_DEFAULT_ACCESS: deny-all
volumes:
- ./ntfy-cache:/var/cache/ntfy
- ./ntfy-data:/var/lib/ntfy
ports:
- "80:80"
restart: unless-stopped
ntfy.example.com {
reverse_proxy ntfy:80
}
Backup ntfy-data/ for user accounts
curl -s http://localhost:80/v1/health
Share sensitive info via self-destructing links. Secret encrypted in-browser, stored temporarily, destroyed after viewing.
Sender --> OTS (:7143) --> Secret Link
|
View Once
Then Destroy
services:
ots:
image: onetimesecret/ots:latest
container_name: ots
ports:
- "7143:443"
environment:
APP_HOST: ots.example.com
SSL_ENABLED: "false"
restart: unless-stopped
ots.example.com {
reverse_proxy ots:443 { transport http { tls_insecure_skip_verify } }
}
Stateless - secrets destroyed after viewing. No persistent data.
curl -sk https://localhost:7143/
Run a Tor relay to help the Tor network. Non-exit relay uses minimal resources and contributes to internet freedom.
Tor Network <--> Your Relay (:9001) <--> Tor Network
|
ORPort 9001
DirPort 9030
services:
tor-relay:
image: osminogin/tor-simple:latest
container_name: tor-relay
volumes:
- ./torrc:/etc/tor/torrc:ro
- ./tor-data:/var/lib/tor
ports:
- "9001:9001"
- "9030:9030"
restart: unless-stopped
# Not proxied - Tor uses its own network
Backup tor-data/ for relay keys (preserves reputation)
curl --socks5-hostname localhost:9050 https://check.torproject.org
Save web articles to read later, stripped of ads. Self-hosted Pocket alternative with full-text search, tags, ePUB/PDF export.
+--------+ HTTPS +---------+ +--------+ |Browser |<===========>| Wallabag|<===>|MariaDB | | App | | :8080 | | + Redis| +--------+ +---------+ +--------+
services:
wallabag:
image: wallabag/wallabag:latest
container_name: wallabag
environment:
SYMFONY__ENV__DATABASE_DRIVER: pdo_mysql
SYMFONY__ENV__DATABASE_HOST: wallabag-db
SYMFONY__ENV__DATABASE_NAME: wallabag
SYMFONY__ENV__DATABASE_USER: wallabag
SYMFONY__ENV__DATABASE_PASSWORD: changeme
SYMFONY__ENV__FOSUSER_REGISTRATION: "false"
volumes:
- ./wallabag-images:/var/www/wallabag/web/assets/images
ports:
- "8080:80"
depends_on: [wallabag-db]
restart: unless-stopped
wallabag-db:
image: mariadb:latest
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: wallabag
MYSQL_USER: wallabag
MYSQL_PASSWORD: changeme
volumes:
- ./wallabag-db:/var/lib/mysql
restart: unless-stopped
wallabag.example.com {
reverse_proxy wallabag:80
}
mysqldump wallabag > backup.sql. Also backup wallabag-images/
curl -s http://localhost:8080/ | grep -q wallabag
Personal minimalist bookmarking platform. PHP-based, file storage (no database), incredibly light. Save links with tags and notes.
+--------+ HTTPS +--------+ +--------+ |Browser |<===========>|Shaarli |<===>| JSON | |Ext. | | :80 | | Files | +--------+ +--------+ +--------+
services:
shaarli:
image: shaarli/shaarli:latest
container_name: shaarli
volumes:
- ./shaarli-data:/var/www/shaarli/data
ports:
- "8080:80"
restart: unless-stopped
shaarli.example.com {
reverse_proxy shaarli:80
}
Backup shaarli-data/ directory. File-based, simple tar/zip.
curl -s http://localhost:80/ | grep -q Shaarli
Self-hosted push notification server with Android app and CLI client. Send messages from scripts directly to your phone.
Script/API --> Gotify (:8080) --> Android App
|
WebSocket
Push
services:
gotify:
image: gotify/server:latest
container_name: gotify
volumes:
- ./gotify-data:/app/data
ports:
- "8080:80"
environment:
GOTIFY_DEFAULTUSER_PASS: changeme
restart: unless-stopped
gotify.example.com {
reverse_proxy gotify:80
}
Backup gotify-data/ directory (messages and config)
curl -s http://localhost:80/health