This is a snapshot of my personal homelab setup. The repository primarily serves as my own reference for infrastructure configurations, Podman setups, and service management workflows. While not a formal guide, I hope others might find parts of this useful for inspiration or adaptation.
With the hardware update of the home server, I plan to transfer everything to Nix.
My sister’s dog chewed through my old laptop’s monitor one day. Instead of fixing it, I repurposed the machine as a server for personal projects. This accident sparked my journey into self-hosted services and eventually led to building this homelab.
This repository exists to document my infrastructure configurations, network diagrams, and notes. It helps me track changes, rebuild systems quickly, and share how my setup works.
The infrastructure is divided into two main machines: a public-facing proxy server and a homelab virtual machine (VM) hosted on Proxmox. All external traffic directed to my domain starts at the proxy server, where Traefik terminates TLS connections and forwards requests to the homelab VM via a WireGuard tunnel.
Inside the Proxmox host, the homelab VM and a dedicated Pi-hole container coexist. The Pi-hole serves as the DNS resolver for the local network, handling ad-blocking and resolving custom domain names (e.g., service.local). These local domains are routed to the homelab VM, where a secondary Traefik instance directs traffic to the appropriate containers based on the requested domain. For example, a query for grafana.local is resolved by Pi-hole to the homelab’s IP address, then proxied by Traefik to the Grafana container.
Hostname: Proxmox
| Component | Specification |
|---|---|
| CPU | 4 x Intel(R) Core(TM) i3-2330M CPU @ 2.20GHz |
| GPU | GeForce GT540M |
| RAM | 8GB DDR3 1333MHz |
| Storage | HDD 465.8G |
| OS | Proxmox 8 |
Homelab hardware limitations
This setup is hosted on a modest home lab server with limited resources, which may not meet the demands of high-performance or production-grade workloads. While certain services might experience slower response times or reduced efficiency, the configuration works adequately for my personal, low-traffic use cases. Compromises in speed and scalability are expected, but the system meets my current needs without critical issues.Hostname: proxy
| Component | Specification |
|---|---|
| CPU | 1 x 3.3 GHz CPU |
| RAM | 1GB |
| Storage | SSD 15G |
| OS | Ubuntu Server 25.04 |
The infrastructure organizes its filesystem to streamline container management, data separation, and media organization. On both the proxy server and the homelab VM, two primary directories are used: /data stores persistent data volumes for containers—such as databases, configuration files, or service-specific storage, while /stacks centralizes compose files and environment variable definitions (.env) for each service.
On the homelab VM, an additional /media directory exists to house media files. Its subdirectory structure follows conventions from trash-guides.
Some services, such as Traefik, Dockge, and Diun, work using systemd to ensure that they start automatically after the system starts, while other services are started manually inside Dockge if they are stopped or the system is restarted.
- proxy.network
[Network]
NetworkName=proxy- traefik.container
[Unit]
Description=The Cloud Native Application Proxy
[Container]
AutoUpdate=registry
ContainerName=traefik
Image=docker.io/traefik:latest
Label=docker.group=traefik traefik.enable=true traefik.http.routers.traefik.rule=Host(`traefik.example.com`) traefik.http.routers.traefik.entrypoints=websecure traefik.http.routers.traefik.service=api@internal traefik.http.routers.traefik.tls=true traefik.http.routers.traefik.tls.certresolver=letsencrypt traefik.http.services.traefik.loadbalancer.server.port=8080 traefik.http.routers.traefik.middlewares=authelia@docker
Network=proxy
PublishPort=22:2222
PublishPort=80:8080
PublishPort=443:8443
PublishPort=<ip:port>:8082
Volume=/run/user/1000/podman/podman.sock:/var/run/docker.sock:z
Volume=/data/traefik/traefik.yml:/traefik.yml:ro
Volume=/data/traefik/config.yml:/config.yml:ro
Volume=/data/traefik/acme.json:/acme.json
Volume=/data/traefik/logs:/var/log/traefik
[Service]
Restart=always
[Install]
WantedBy=multi-user.target default.target- dockge.container
[Unit]
Description=A fancy, easy-to-use and reactive self-hosted docker compose.yaml stack-oriented manager
[Container]
AutoUpdate=registry
ContainerName=dockge
Label=docker.group=dockge
Environment=DOCKGE_STACKS_DIR=/stacks
Image=docker.io/louislam/dockge:latest
PublishPort=<ip:port>:5001
Volume=/run/user/1000/podman/podman.sock:/var/run/docker.sock:z
Volume=/stacks:/stacks
Volume=/data/dockge:/app/data
[Service]
Restart=always
[Install]
WantedBy=multi-user.target default.target- diun.container
[Unit]
Description=Receive notifications when an image is updated on a Docker registry
[Container]
ContainerName=diun
AutoUpdate=registry
Label=docker.group=diun
Environment=DIUN_NOTIF_GOTIFY_ENDPOINT=https://gotify.bagoont.ru DIUN_NOTIF_GOTIFY_TOKENFILE=/run/secrets/gotify-token DIUN_NOTIF_GOTIFY_PRIORITY=10 DIUN_WATCH_WORKERS=20 "DIUN_WATCH_SCHEDULE=0 */6 * * *" DIUN_WATCH_JITTER=30s DIUN_PROVIDERS_DOCKER=true
Exec=serve
Image=docker.io/crazymax/diun:latest
Secret=gotify-token
Volume=/data/diun:/data
Volume=/run/user/1000/podman/podman.sock:/var/run/docker.sock
[Service]
Restart=always
[Install]
WantedBy=multi-user.target default.target- proxy.network
[Network]
NetworkName=proxy- traefik.container
[Unit]
Description=The Cloud Native Application Proxy
[Container]
ContainerName=traefik
Image=docker.io/traefik:latest
Label=docker.group=traefik traefik.enable=true traefik.http.routers.traefik.rule=Host(`traefik.local`) traefik.http.routers.traefik.entrypoints=web traefik.http.routers.traefik.service=api@internal traefik.http.services.traefik.loadbalancer.server.port=8080
Network=proxy
PublishPort=80:80
PublishPort=443:443
Volume=/run/user/1000/podman/podman.sock:/var/run/docker.sock:z
Volume=/data/traefik/traefik.yml:/traefik.yml:ro
Volume=/data/traefik/config.yml:/config.yml:ro
Volume=/data/traefik/logs:/var/log/traefik
[Service]
Restart=always
[Install]
WantedBy=multi-user.target default.target- dockge.container
[Unit]
Description=A fancy, easy-to-use and reactive self-hosted docker compose.yaml stack-oriented manager
[Container]
AutoUpdate=registry
ContainerName=dockge
Environment=DOCKGE_STACKS_DIR=/stacks
Image=docker.io/louislam/dockge:latest
Label=docker.group=dockge traefik.enable=true traefik.http.routers.dockge.rule=Host(`dockge.local`) traefik.http.routers.dockge.entrypoints=web traefik.http.services.dockge.loadbalancer.server.port=5001
Network=proxy
Volume=/run/user/1000/podman/podman.sock:/var/run/docker.sock:z
Volume=/stacks:/stacks
Volume=/data/dockge:/app/data
[Service]
Restart=always
[Install]
WantedBy=multi-user.target default.target- diun.container
[Unit]
Description=Receive notifications when an image is updated on a Docker registry
[Container]
ContainerName=diun
AutoUpdate=registry
Label=docker.group=diun
Environment=DIUN_NOTIF_GOTIFY_ENDPOINT=https://gotify.bagoont.ru DIUN_NOTIF_GOTIFY_TOKENFILE=/run/secrets/gotify-token DIUN_NOTIF_GOTIFY_PRIORITY=10 DIUN_WATCH_WORKERS=20 "DIUN_WATCH_SCHEDULE=0 */6 * * *" DIUN_WATCH_JITTER=30s DIUN_PROVIDERS_DOCKER=true
Exec=serve
Image=docker.io/crazymax/diun:latest
Secret=gotify-token
Volume=/data/diun:/data
Volume=/run/user/1000/podman/podman.sock:/var/run/docker.sock
[Service]
Restart=always
[Install]
WantedBy=multi-user.target default.targetAuthelia/RedisGitea/Act Runner/PostgreSQLGotifyHomerKarakeep/Chrome/MeilisearchJellyfin/Prowlarr/Lidarr/Flaresolverr/Shadowsocks/qBittorrentGrafana/InfluxDB/Telegraf/PrometheusNextcloud/Redis/PostgreSQLOpengistSyncthingUptime KumaVaultwarden
As noted earlier, this repository serves primarily as a personal project. However, I’m always open to feedback and grateful for any suggestions related to optimization, security improvements, or best practices. If you have ideas to enhance this setup, feel free to open an issue for discussion.
