feat: initial homelab docker stack commit

Sets up the complete self-hosted infrastructure on Voyager (HP t630 thin client):

DNS Stack (dns/):
- Pi-hole for network-wide ad blocking
- Unbound as recursive DNS resolver
- dnscrypt-proxy for DNS-over-HTTPS via Cloudflare/Quad9

Services:
- Vaultwarden - self-hosted password manager (Bitwarden compatible)
- Forgejo - self-hosted git mirror (primary on PMS1, mirror here)
- Karakeep - self-hosted bookmark manager
- Resilio Sync - P2P sync for PMS1 database backups

Tunneling:
- Newt - Pangolin tunnel client for exposing services via
  tunnel.pelagiamarine.com without open ports

All services exposed externally via Pangolin reverse proxy on PMS1.
Local DNS resolves through Pi-hole → Unbound → dnscrypt-proxy chain.
This commit is contained in:
Hardik 2026-06-03 23:45:19 +00:00
commit 10b772807d
13 changed files with 305 additions and 0 deletions

18
.gitignore vendored Normal file
View file

@ -0,0 +1,18 @@
# Forgejo data
forgejo/data/
# Vaultwarden data
vaultwarden/vw-data/
# Pi-hole data
dns/etc-pihole/
# Resilio data and config (contains keys/identity)
resilio/config/
resilio/sync/
# Karakeep data
karakeep/data/
# dnscrypt cache
dns/dnscrypt/cache/

57
dns/compose.yaml Normal file
View file

@ -0,0 +1,57 @@
networks:
dns_net:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/24
services:
dnscrypt-proxy:
image: klutchell/dnscrypt-proxy:latest
container_name: dnscrypt-proxy
restart: unless-stopped
networks:
dns_net:
ipv4_address: 172.20.0.3
volumes:
- ./dnscrypt/dnscrypt-proxy.toml:/config/dnscrypt-proxy.toml:ro
- ./dnscrypt/cache:/config
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8053"]
interval: 10s
timeout: 5s
retries: 5
unbound:
build: ./unbound
container_name: unbound
restart: unless-stopped
networks:
dns_net:
ipv4_address: 172.20.0.2
volumes:
- ./unbound/unbound.conf:/etc/unbound/unbound.conf:ro
- ./unbound/root.hints:/etc/unbound/root.hints:ro
pihole:
image: pihole/pihole:latest
container_name: pihole
restart: unless-stopped
networks:
dns_net:
ipv4_address: 172.20.0.4
ports:
- "53:53/tcp"
- "53:53/udp"
- "80:80/tcp"
- "443:443/tcp"
environment:
TZ: 'Asia/Kolkata'
FTLCONF_webserver_api_password: 'changeme'
FTLCONF_dns_listeningMode: 'all'
FTLCONF_dns_upstreams: '172.20.0.2#5335'
volumes:
- ./etc-pihole:/etc/pihole
cap_add:
- NET_ADMIN

View file

@ -0,0 +1,14 @@
# Listen on all interfaces inside the container
listen_addresses = ['0.0.0.0:5053']
# Use these DoH servers (both no-logs, DNSSEC)
server_names = ['cloudflare', 'quad9-doh-ip4-port443-filter-ecs-pri']
[sources.public-resolvers]
urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/public-resolvers.md',
'https://download.dnscrypt.info/resolvers-list/v3/public-resolvers.md']
cache_file = '/config/public-resolvers.md'
minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
[anonymized_dns]
skip_incompatible = true

3
dns/unbound/Dockerfile Normal file
View file

@ -0,0 +1,3 @@
FROM alpine:latest
RUN apk add --no-cache unbound
ENTRYPOINT ["unbound", "-d", "-c", "/etc/unbound/unbound.conf"]

92
dns/unbound/root.hints Normal file
View file

@ -0,0 +1,92 @@
; This file holds the information on root name servers needed to
; initialize cache of Internet domain name servers
; (e.g. reference this file in the "cache . <file>"
; configuration file of BIND domain name servers).
;
; This file is made available by InterNIC
; under anonymous FTP as
; file /domain/named.cache
; on server FTP.INTERNIC.NET
; -OR- RS.INTERNIC.NET
;
; last update: May 21, 2026
; related version of root zone: 2026052101
;
; FORMERLY NS.INTERNIC.NET
;
. 3600000 NS A.ROOT-SERVERS.NET.
A.ROOT-SERVERS.NET. 3600000 A 198.41.0.4
A.ROOT-SERVERS.NET. 3600000 AAAA 2001:503:ba3e::2:30
;
; FORMERLY NS1.ISI.EDU
;
. 3600000 NS B.ROOT-SERVERS.NET.
B.ROOT-SERVERS.NET. 3600000 A 170.247.170.2
B.ROOT-SERVERS.NET. 3600000 AAAA 2801:1b8:10::b
;
; FORMERLY C.PSI.NET
;
. 3600000 NS C.ROOT-SERVERS.NET.
C.ROOT-SERVERS.NET. 3600000 A 192.33.4.12
C.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:2::c
;
; FORMERLY TERP.UMD.EDU
;
. 3600000 NS D.ROOT-SERVERS.NET.
D.ROOT-SERVERS.NET. 3600000 A 199.7.91.13
D.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:2d::d
;
; FORMERLY NS.NASA.GOV
;
. 3600000 NS E.ROOT-SERVERS.NET.
E.ROOT-SERVERS.NET. 3600000 A 192.203.230.10
E.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:a8::e
;
; FORMERLY NS.ISC.ORG
;
. 3600000 NS F.ROOT-SERVERS.NET.
F.ROOT-SERVERS.NET. 3600000 A 192.5.5.241
F.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:2f::f
;
; FORMERLY NS.NIC.DDN.MIL
;
. 3600000 NS G.ROOT-SERVERS.NET.
G.ROOT-SERVERS.NET. 3600000 A 192.112.36.4
G.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:12::d0d
;
; FORMERLY AOS.ARL.ARMY.MIL
;
. 3600000 NS H.ROOT-SERVERS.NET.
H.ROOT-SERVERS.NET. 3600000 A 198.97.190.53
H.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:1::53
;
; FORMERLY NIC.NORDU.NET
;
. 3600000 NS I.ROOT-SERVERS.NET.
I.ROOT-SERVERS.NET. 3600000 A 192.36.148.17
I.ROOT-SERVERS.NET. 3600000 AAAA 2001:7fe::53
;
; OPERATED BY VERISIGN, INC.
;
. 3600000 NS J.ROOT-SERVERS.NET.
J.ROOT-SERVERS.NET. 3600000 A 192.58.128.30
J.ROOT-SERVERS.NET. 3600000 AAAA 2001:503:c27::2:30
;
; OPERATED BY RIPE NCC
;
. 3600000 NS K.ROOT-SERVERS.NET.
K.ROOT-SERVERS.NET. 3600000 A 193.0.14.129
K.ROOT-SERVERS.NET. 3600000 AAAA 2001:7fd::1
;
; OPERATED BY ICANN
;
. 3600000 NS L.ROOT-SERVERS.NET.
L.ROOT-SERVERS.NET. 3600000 A 199.7.83.42
L.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:9f::42
;
; OPERATED BY WIDE
;
. 3600000 NS M.ROOT-SERVERS.NET.
M.ROOT-SERVERS.NET. 3600000 A 202.12.27.33
M.ROOT-SERVERS.NET. 3600000 AAAA 2001:dc3::35
; End of file

22
dns/unbound/unbound.conf Normal file
View file

@ -0,0 +1,22 @@
server:
verbosity: 1
interface: 0.0.0.0
port: 5335
do-ip4: yes
do-udp: yes
do-tcp: yes
do-ip6: no
access-control: 0.0.0.0/0 allow
root-hints: "/etc/unbound/root.hints"
harden-glue: yes
harden-dnssec-stripped: yes
use-caps-for-id: yes
edns-buffer-size: 1472
prefetch: yes
num-threads: 1
hide-identity: yes
hide-version: yes
forward-zone:
name: "."
forward-addr: 172.20.0.3@5053

View file

@ -0,0 +1 @@

14
forgejo/compose.yml Normal file
View file

@ -0,0 +1,14 @@
services:
forgejo:
image: codeberg.org/forgejo/forgejo:10
container_name: forgejo
restart: unless-stopped
environment:
- USER_UID=1000
- USER_GID=1000
- FORGEJO__database__DB_TYPE=sqlite3
volumes:
- ./data:/data
ports:
- 3000:3000
- 22222:22 # SSH for git push/pull

4
karakeep/.env Normal file
View file

@ -0,0 +1,4 @@
KARAKEEP_VERSION=release
NEXTAUTH_SECRET=myNVIxjmYr46m3ZPSzC2//PSfAvYtANvuxGFGBcnwbvQGlNx
MEILI_MASTER_KEY=KYq5jf6aygLGUW8PAVVGcDzH+/bvlGgMsT8BSTmqUhafwecL
NEXTAUTH_URL=https://bookmarks.tunnel.pelagiamarine.com

View file

@ -0,0 +1,44 @@
services:
web:
image: ghcr.io/karakeep-app/karakeep:${KARAKEEP_VERSION:-release}
restart: unless-stopped
volumes:
# By default, the data is stored in a docker volume called "data".
# If you want to mount a custom directory, change the volume mapping to:
# - /path/to/your/directory:/data
- data:/data
ports:
- 3333:3000
env_file:
- .env
environment:
MEILI_ADDR: http://meilisearch:7700
BROWSER_WEB_URL: http://chrome:9222
OPENAI_API_KEY: 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
# You almost never want to change the value of the DATA_DIR variable.
# If you want to mount a custom directory, change the volume mapping above instead.
DATA_DIR: /data # DON'T CHANGE THIS
chrome:
image: gcr.io/zenika-hub/alpine-chrome:124
restart: unless-stopped
command:
- --no-sandbox
- --disable-gpu
- --disable-dev-shm-usage
- --remote-debugging-address=0.0.0.0
- --remote-debugging-port=9222
- --hide-scrollbars
meilisearch:
image: getmeili/meilisearch:v1.41.0
restart: unless-stopped
env_file:
- .env
environment:
MEILI_NO_ANALYTICS: "true"
volumes:
- meilisearch:/meili_data
volumes:
meilisearch:
data:

9
newt/docker-compose.yml Normal file
View file

@ -0,0 +1,9 @@
services:
newt:
image: fosrl/newt
container_name: newt
restart: unless-stopped
environment:
- PANGOLIN_ENDPOINT=https://pangolin.pelagiamarine.com
- NEWT_ID=0rq10j85e7hbh4g
- NEWT_SECRET=orv1rdmsm0ykkoee5ayyzmpxvxq2vss5w5qjf2pkx6rb6ddl

16
resilio/compose.yml Normal file
View file

@ -0,0 +1,16 @@
services:
resilio:
image: lscr.io/linuxserver/resilio-sync:latest
container_name: resilio
restart: unless-stopped
environment:
- PUID=1000
- PGID=1000
- TZ=Asia/Kolkata
volumes:
- ./config:/config
- /home/shad0w/backups:/sync/backups
ports:
- 8888:8888
- 55555:55555

11
vaultwarden/compose.yml Normal file
View file

@ -0,0 +1,11 @@
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
environment:
DOMAIN: "https://vaultwarden.tunnel.pelagiamarine.com"
volumes:
- ./vw-data/:/data/
ports:
- 8000:80