A personal homelab running on a single laptop โ infrastructure and applications managed entirely as code, secured by design, and built for reliability without the overhead of a data center.
| Path | Description |
|---|---|
| Internet | Users access services through Cloudflare. Cloudflare checks the user's email and sends a one-time code to verify them. Once verified, traffic goes through the tunnel into the server, where NGINX sends each request to the right service. |
| Tailscale | Devices connected to the Tailscale network can access services directly with randomized ports. The server only allows certain ports for laptops and phones, and shared users can only reach some services. |
This project is built around a few core principles:
Infrastructure as Code.
Everything that can be codified, is. Terraform manages DNS, tunnels, access policies, and network ACLs. Docker Compose files define every service with explicit configuration and resource limits. There are no SSH-and-pray moments.
Defense in depth.
Default service ports are never used โ every port is randomized. Tailscale provides a zero-trust overlay network with ACL-enforced access control. Cloudflare Tunnel fronts all public traffic, with Cloudflare Access requiring email-based authentication before any request reaches the server. The result is multiple independent layers of security.
Segregation of concerns.
Services are split into two categories: infrastructure (what keeps the server running and observable) and applications (what makes the server useful). This separation makes it clear which services are foundational and which are the actual tools being hosted.
Explicit resource governance.
Every single container has CPU and memory limits. No service can spike and starve another. The lab stays predictable under load.
| Service | Used for | |
|---|---|---|
| โ๏ธ | cloudflared | Connects the server to Cloudflare's edge network, routing public traffic through a secure tunnel so services are accessible without exposing the server directly |
| ๐ | nginx | Acts as a reverse proxy, reading the incoming domain and forwarding each request to the correct internal service |
| ๐ | dozzle | Streams live logs from every running container into a browser dashboard for debugging and monitoring |
| ๐ | btop | Exposes a real-time system monitor through the browser so you can check CPU, memory, and processes without SSH |
| ๐ | filebrowser | Provides a web-based file manager to browse, upload, and edit files across the entire server |
| โฐ | ofelia | Runs scheduled jobs inside containers using Docker labels, used here to trigger backups automatically |
| ๐พ | rclone | Runs as a REST server that receives and stores backup data from Restic |
| ๐ | restic | Backs up Satisfactory game data to the Rclone server every 20 minutes, keeping saves safe from corruption or crashes |
| Service | Used for | |
|---|---|---|
| ๐ฎ | satisfactory | Runs a dedicated Satisfactory game server that friends can join at any time, with automatic backups and configurable player limits |
| ๐น | ytdlp | Provides a web interface for yt-dlp to download videos from various platforms directly to the server |
services/
โโโ {infra|apps}/
โโโ <service-name>/
โโโ configuration/ # Static config files mounted into the container
โโโ .env # Local environment values (gitignored)
โโโ .env.example # Documented variables with placeholder values
โโโ compose.yml # Service definition with ports, volumes, networks, resources
Every service that needs static config files places them in a configuration subdirectory. This keeps mounts predictable and organized, with a single place to look for a service's settings.
# services/infra/nginx/compose.yml
volumes:
- ./configuration/nginx.conf.template:/etc/nginx/templates/nginx.conf.template:roVariables are prefixed with the service name to avoid collisions and make it clear what each one controls.
# services/apps/satisfactory/.env.example
SATISFACTORY_MAX_PLAYERS=4
SATISFACTORY_GAME_PORT=7328
SATISFACTORY_MESSAGING_PORT=1273Infrastructure services handle monitoring, networking, and backups. Application services are the actual tools being hosted. This split keeps concerns clean โ you know what keeps the server running versus what makes it useful.
services/
โโโ infra/ # monitoring, networking, backups, access
โ โโโ btop/
โ โโโ cloudflared/
โ โโโ dozzle/
โ โโโ nginx/
โ โโโ ofelia/
โ โโโ rclone/
โ โโโ restic/
โโโ apps/ # user-facing tools
โโโ satisfactory/
โโโ ytdlp/
Every service declares how much CPU and memory it can use. This prevents any single service from spiking and starving the others, keeping the whole lab stable under load.
# services/infra/dozzle/compose.yml
deploy:
resources:
limits:
cpus: '0.5'
memory: 64MNo service uses its default port. Every exposed port is set through an environment variable, making it easy to change and harder for automated scans to find services. Internal ports stay at their well-known defaults; only the externally-facing port is randomized.
# services/infra/btop/compose.yml
ports:
- "${BTOP_PORT}:7681"All services join a shared Docker network so they can resolve each other by container name without complex networking setup.
# shared pattern across all compose.yml files
networks:
lab:
name: lab
external: trueEvery container uses restart policies so services recover from crashes or host reboots without manual intervention. Container names are set explicitly to match their Compose service key, keeping references unambiguous across the stack.
# shared pattern across all compose.yml files
restart: unless-stopped
container_name: <service-name>- Docker and Docker Compose
- Terraform โฅ 1.0
- A Cloudflare account with a zone configured
- A Tailscale tailnet with devices enrolled
# 1. Create the shared Docker network
make setup
# 2. Bootstrap Terraform
make terraform-init
# 3. Review and apply infrastructure changes
make terraform-plan
make terraform-apply
# 4. Start infrastructure services
make infra-up
# 5. Start application services
make apps-up| Command | Description |
|---|---|
setup |
Create the lab Docker network |
infra-up |
Start all infrastructure services |
infra-up-build |
Start infra services with rebuild |
infra-down |
Stop all infrastructure services |
apps-up |
Start all application services |
apps-up-build |
Start app services with rebuild |
apps-down |
Stop all application services |
terraform-init |
Initialize Terraform |
terraform-init-upgrade |
Re-initialize with provider upgrades |
terraform-plan |
Preview infrastructure changes |
terraform-apply |
Apply infrastructure changes |
MIT โ free to use, modify, and distribute as you see fit.