1. Why This Homelab Exists – One Laptop, Many Services, Zero Port Forwarding
I had an “old” laptop with a decent CPU and RAM lying around and wanted to turn it into something actually useful: a 24/7 homelab where I can run tools like Nextcloud, Grafana or n8n without paying for a VPS every month.
The constraints:
- No noisy GUI – the machine lives in a corner, headless.
- All services should be containerized using Docker Compose.
- Remote access must be safe and simple, ideally without touching the ISP router.
- Everything should be documented and reproducible so I can rebuild it later.
The result is this repo: a Docker-based homelab on Debian 13, with
Cloudflare Tunnel in front so I can expose selected services to the Internet
using subdomains like grafana.example.com or cloud.example.com – no
open ports, no dynamic DNS drama.
2. Hardware & Base OS – Free Performance on a “Dead” Laptop
The homelab runs on:
- CPU: Intel i7-1255U (12th gen, mobile CPU).
- RAM: 24 GB DDR4, enough for multiple containers.
- Disk: NVMe SSD, hundreds of GB for data and images.
- GPU: Integrated graphics – irrelevant for a headless server.
Operating system:
- Debian 13, no desktop environment.
- All management through SSH and web UIs (Portainer, Cockpit, Grafana, etc.).
On top of the base install, I added a toolbox of packages: Docker / Docker Compose,
programming runtimes (Python, Go, Node, Ruby), Tor, Cockpit, and Cloudflare’s
cloudflared for tunnels. The goal is to be able to spin up almost anything from this
one box.
2.1 Repository Layout
The repo mirrors the way the homelab is organized:
homelab/
├── README.md
├── cloudflared/
│ └── config.yml # Cloudflare Tunnel config (example)
└── docker/
├── code-server/
│ └── docker-compose.yml # VS Code in browser
├── filebrowser/
│ └── docker-compose.yml # Web file manager
├── grafana/
│ └── docker-compose.yml # Dashboards
├── n8n/
│ └── docker-compose.yml # Automation
├── nextcloud/
│ └── docker-compose.yml # Cloud storage + MariaDB + Adminer
├── nginx/
│ └── docker-compose.yml # Simple dashboard
└── portainer/
└── docker-compose.yml # Docker management UI
Each service is isolated in its own folder so you can start, stop and tweak them independently.
3. Docker Services – The Core Apps of the Homelab
Let’s look at what actually runs on the server and how each piece fits in.
3.1 code-server – VS Code in the Browser
I use code-server as a browser-based VS Code instance:
- Port:
8443on the host. - Workspace mapped from a host folder (for example
/home/anass24/code). - Password protection via an env variable in the compose file.
cd docker/code-server
docker compose up -d
Locally I hit https://<server-ip>:8443. Remotely I go through
code.example.com routed by Cloudflare Tunnel.
3.2 filebrowser – Web File Manager for /data
filebrowser gives me a lightweight file manager:
- Port:
8082. - Host data root:
/data. - Its own DB stored inside
/data/filebrowser.
cd docker/filebrowser
docker compose up -d
# Local: http://<server-ip>:8082
# Remote: files.example.com via Cloudflare
3.3 Grafana – Metrics and Dashboards
Grafana is my main dashboard layer:
- Port:
3000. - Data stored in a named Docker volume (e.g.
grafana_data). - Usually paired with Prometheus (not shown here) for metrics.
cd docker/grafana
docker compose up -d
# Local: http://<server-ip>:3000
# Remote: grafana.example.com
3.4 n8n – Automation Workbench
n8n runs as my low-code automation hub:
- Port:
5678. - Persistent data under
/data/n8non the host. - Timezone set to
Europe/Madridso crons and timestamps match my life.
cd docker/n8n
docker compose up -d
# Local: http://<server-ip>:5678
# Remote: n8n.example.com
In development I keep N8N_SECURE_COOKIE=false to avoid cookie issues behind plain
HTTP, then switch to true once everything sits behind HTTPS.
3.5 Nextcloud Stack – Files + MariaDB + Adminer
The Nextcloud stack bundles three containers:
- mariadb – database.
- nextcloud – main web app.
- adminer – tiny DB management UI.
Ports:
- Nextcloud:
8081. - Adminer:
8083. - MariaDB (internal):
3306.
cd docker/nextcloud
docker compose up -d
# Local:
# Nextcloud: http://<server-ip>:8081
# Adminer: http://<server-ip>:8083 (server: mariadb)
Before first start I always go through the compose file and replace all the
CHANGE_ME_... values: DB root password, Nextcloud admin credentials, trusted
domains and so on.
3.6 nginx Dashboard – Simple Landing Page for the Lab
A tiny nginx container serves a static dashboard:
- Port:
8080. - Root file:
docker/nginx/index.html.
cd docker/nginx
# put your dashboard HTML into docker/nginx/index.html
docker compose up -d
# Local: http://<server-ip>:8080
# Remote: dashboard.example.com
That index page is just a clean landing page with links to everything: Nextcloud, filebrowser, Grafana, n8n, Portainer, Cockpit… basically a control panel for the homelab.
3.7 Portainer – Docker Management UI
Portainer gives me a visual way to inspect containers, volumes and networks:
- HTTP:
9000, HTTPS:9443. - Mounts the Docker socket to control the local engine.
- Stores its data in a named volume (e.g.
portainer_data).
cd docker/portainer
docker compose up -d
# Local:
# http://<server-ip>:9000
# https://<server-ip>:9443
# Remote:
# docker.example.com (via Cloudflare)
3.8 Cockpit – Host-Level Web UI
Cockpit is installed directly on Debian (not as a container). It runs on a
custom port (for example 9091) and gives a nice web view over systemd services,
logs and storage.
It’s also exposed through Cloudflare Tunnel as cockpit.example.com, but locked
behind strong authentication.
4. Cloudflare Tunnel – Publishing Services Without Opening Ports
Instead of forwarding ports from my home router, I use a single Cloudflare Tunnel that connects the server to Cloudflare’s edge. From there, subdomains on my zone are mapped to local services.
4.1 Example cloudflared/config.yml
The config file defines the tunnel ID and the ingress rules:
loglevel: info
tunnel: <YOUR_TUNNEL_ID>
credentials-file: /root/.cloudflared/<YOUR_TUNNEL_ID>.json
ingress:
- hostname: grafana.example.com
service: http://localhost:3000
- hostname: n8n.example.com
service: http://localhost:5678
- hostname: cloud.example.com
service: http://localhost:8081
- hostname: files.example.com
service: http://localhost:8082
- hostname: sql.example.com
service: http://localhost:8083
- hostname: code.example.com
service: http://localhost:8443
- hostname: docker.example.com
service: http://localhost:9000
- hostname: cockpit.example.com
service: http://localhost:9091
- hostname: dashboard.example.com
service: http://localhost:8080
- service: http_status:404
Each subdomain points to a port on localhost. Cloudflare handles TLS certificates
and DNS; the server only needs to talk plain HTTP on the LAN side.
4.2 Creating the Tunnel and DNS Routes
The basic flow:
# 1) Login (opens browser)
cloudflared tunnel login
# 2) Create a tunnel
cloudflared tunnel create homelab
# 3) List and grab the ID
cloudflared tunnel list
After updating config.yml with the tunnel ID, I link each hostname with the tunnel:
cloudflared tunnel route dns homelab grafana.example.com
cloudflared tunnel route dns homelab n8n.example.com
cloudflared tunnel route dns homelab cloud.example.com
cloudflared tunnel route dns homelab files.example.com
cloudflared tunnel route dns homelab sql.example.com
cloudflared tunnel route dns homelab code.example.com
cloudflared tunnel route dns homelab docker.example.com
cloudflared tunnel route dns homelab cockpit.example.com
cloudflared tunnel route dns homelab dashboard.example.com
4.3 Running cloudflared as a Service
Finally I install it as a system service so the tunnel starts on boot:
sudo cloudflared service install
sudo systemctl enable cloudflared
sudo systemctl start cloudflared
sudo systemctl status cloudflared
From that point on, the homelab behaves like a mini SaaS environment behind my own domain, but with zero open ports and full control.
5. From Fresh Debian to Full Homelab – Bootstrap Checklist
If I had to rebuild this homelab from scratch on a new machine, this would be the order:
- Install Debian 13 (minimal), enable SSH.
- Install system tools, Docker / Compose, Tor, Cockpit,
cloudflaredand runtimes (Python, Go, Node, Ruby). - Create base directories:
/data(root for shared storage).- Subfolders like
/data/filebrowser,/data/nextcloud,/data/mariadb,/data/n8n. - A code workspace folder, e.g.
/home/<user>/code.
- Clone the repo onto the server:
git clone https://github.com/ciscoAnass/homelab.git cd homelab - Edit all compose files and replace:
CHANGE_ME_...values with strong passwords.example.comwith your real domain.
- Bring up each Docker stack:
cd docker/<service> docker compose up -d - Configure Cloudflare Tunnel and DNS mappings using
cloudflared/config.yml. - Harden access with Cloudflare Zero Trust / Access, strong passwords and regular updates.
6. Security Checklist & Next Steps
Even for a personal homelab, I treat this like a small production system. A few rules I follow:
6.1 Security Checklist
- Passwords: never run with the default placeholder secrets – rotate everything before the first
docker compose up. - Domains: stick to a dedicated domain or subdomain only for the homelab; don’t mix it with random personal sites.
- Cloudflare Access: gate sensitive UIs (Portainer, Cockpit, n8n) with SSO / MFA if possible.
- Updates: regularly patch Debian and refresh containers:
sudo apt update && sudo apt upgrade- Pull new images and
docker compose up -dagain.
- Backups: back up:
/data(Nextcloud files, DBs, n8n data, filebrowser DB).- Named volumes like
grafana_data,portainer_data.
6.2 What I Want to Add Next
- Central monitoring: Prometheus + Loki + Grafana for logs and metrics from all containers.
- Offsite backups: encrypted snapshots to another location (cloud or a friend’s server).
- More services: Jellyfin for media, a password manager vault, maybe a small Kubernetes lab.
- IaC: move more of this setup to Ansible / Terraform to recreate the homelab automatically.
For now, this homelab is exactly what I wanted: a single Debian box that behaves like my own little datacenter, exposed safely through Cloudflare and defined as code inside one public repo.
💬 Want to discuss homelabs or this stack?
Reach out on LinkedIn or send me an email. I’m always happy to talk about homelab design, Cloudflare Tunnel, and self-hosting workflows on low-power hardware.