Skip to content

mathdejesus/homelab

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 

Repository files navigation

🏠 Homelab — Personal Infrastructure Server

A self-hosted homelab running on a repurposed notebook, built as a functional development infrastructure and a hands-on portfolio for roles in Linux system administration and cybersecurity.


📋 Overview

This repository documents my home server setup: Docker Compose stacks, maintenance scripts, and architectural decisions. The project serves a dual purpose — real personal utility (development databases, container management, monitoring) and demonstrable portfolio evidence of infrastructure skills.

Philosophy: Direct control over every layer. No managed platforms, no unnecessary abstraction. Understand the why behind every command.


🖥️ Hardware

Component Details
Model Samsung NP350XAA (headless)
CPU Intel Core i5-8250U
RAM ~3.7 GB usable (constrained env)
Storage 1 TB SSD
Partition LVM (55 GB root · 831 GB /home)
Encryption LUKS2 full-disk encryption

Running headless on Debian 13 (Trixie). SSH access via Tailscale VPN.


🛠️ Stack

All services are orchestrated with Docker Compose (v5.1.2 plugin — docker compose, not docker-compose).

homelab/
├── docker-compose/
│   ├── compose.dev.yml        # Active dev stack
│   └── docker-compose.yml     # Legacy stack (archived)
└── scripts/
    └── update-all.sh          # Full system update script

Active Services

Service Image Port Role
PostgreSQL postgres:16 5432 Primary relational database
Redis redis:7 6379 Cache / message broker
Portainer CE portainer/portainer-ce 9443 Container management UI

All containers run with restart: unless-stopped and use named volumes for data persistence.

Runtime Environment (Host)

The server also provides a pre-configured environment for development projects:

Tool Version
Java 21 LTS
Python 3.13
Go 1.24
Node.js LTS

🌐 Networking

  • VPN: Tailscale (primary remote access layer)
  • Firewall: UFW
  • Management UI: Cockpit (port 9090)
  • Client SSH alias: homelab → Tailscale IP (Fedora client machine)

External access is handled entirely through Tailscale — no open ports on the public internet.


🔧 Scripts

scripts/update-all.sh

Single-command full system update. Run with:

update-all

Covers:

Layer Tool Behavior on missing tool
OS packages apt
Python tools pipx upgrade-all warns and skips
Node.js npm update -g warns and skips
Go binaries go install @latest per bin warns and skips
Docker images docker pull warns and skips
Compose stack docker compose pull && up -d warns and skips

The script uses set -euo pipefail with || warn fallbacks — unexpected errors stop execution but missing optional tools are skipped gracefully.


💾 Storage Strategy

  • Named Docker volumes are used for all persistent service data
  • Bind mounts were intentionally avoided — they caused UID/GID conflicts in past deployments (Apache running as www-data, UID 33)
  • Portainer volume declared as external: true to preserve configuration state across stack redeployments

🔑 Key Architectural Decisions

Why Docker Compose instead of Kubernetes (K3s)?

K3s was evaluated and removed. At this hardware scale (~3.7 GB usable RAM), K3s alone consumed ~600 MB with no justified benefit. Docker Compose was chosen for:

  • RAM efficiency — critical in a memory-constrained environment
  • Direct control — no Kubernetes abstraction layer between operator and containers
  • Portfolio signal — demonstrates deliberate architectural reasoning over cargo-culting orchestration tools

Why LUKS2 full-disk encryption?

Despite the operational overhead (manual passphrase entry on boot), LUKS2 encryption was kept because:

  • Protects data-at-rest on a physically accessible device
  • Demonstrates security-conscious infrastructure design
  • Relevant to a cybersecurity-focused portfolio

Why named volumes over bind mounts?

Bind mounts caused 403 Forbidden errors in past deployments due to UID/GID mismatches between the host and container processes. Named volumes delegate ownership to Docker, resolving permission issues cleanly.


📚 Lessons Learned

Problem Root Cause Solution
Apache 403 Forbidden Bind mount UID/GID conflict Switched to named Docker volumes
Port conflict on deploy Pre-existing nginx on port 8080 Audit ports with ss -tlnp before deploying
docker-compose not found Plugin version uses space Use docker compose (v5.1.2 plugin)
Script silently stopping set -euo pipefail + find returning exit 1 on missing dir Guard directory existence before find
Containers not persisting data Missing volume declarations on docker run Migrated to Compose with explicit named volumes

🗺️ Roadmap

  • Migrate from K3s to Docker Compose
  • Configure Tailscale VPN access
  • Deploy dev stack (PostgreSQL, Redis, Portainer)
  • Migrate containers from docker run to Compose with named volumes
  • Write update-all maintenance script
  • Configure HTTPS via Tailscale for the full stack
  • Set up automated backups for named volumes
  • Harden UFW rules and document firewall policy
  • Add monitoring/observability layer (Uptime Kuma or Prometheus stack)

🧰 Host Environment

  • OS: Debian 13 (Trixie), headless — hostname DebianHomeServer
  • Containerization: Docker + Compose plugin v5.1.2
  • VCS: Git + GitHub (SSH key authentication via ed25519)

⚠️ Disclaimer

This is a personal homelab for learning and portfolio purposes. Configuration values (IPs, credentials, domain names) are intentionally omitted or anonymized in this repository. Never commit secrets to version control.


Built and maintained by Matheus Costa de Jesus · LinkedIn

About

Servidor caseiro com Debian, Docker, Kubernetes e monitoramento.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages