Building a FOSS Stack from Scratch
You have a fresh VPS, root access, and a commitment to FOSS. This guide takes you from bare metal to a fully self-hosted ecosystem — code hosting, CI/CD, monitoring, backups, and more — all running on FOSS tools.
Phase 0: Choose Your Base
Recommended VPS: Hetzner CX21 (€5.83/mo: 2 vCPU, 4 GB RAM, 40 GB NVMe) or Netcup RS 1000 G9.5 (€8.99/mo: 4 vCPU, 8 GB RAM, 256 GB SSD).
The Netcup plan gives more headroom for future services. The Hetzner plan handles the core stack with room to spare. Both offer KVM virtualization with full OS control.
Operating System: Debian 12 (stable, well-supported, minimal). Ubuntu 24.04 LTS is fine if you prefer the Ubuntu ecosystem.
Phase 1: Foundation (30 minutes)
apt update && apt upgrade -y
adduser deploy
usermod -aG sudo deploy
apt install -y curl wget git ufw fail2ban
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
Phase 2: Docker + Coolify (15 minutes)
Coolify provides a Heroku-like experience for deploying applications:
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
This installs Docker, Docker Compose, and Coolify in one command. Access the web UI at http://your-server-ip:8000 and complete the initial setup.
Phase 3: Reverse Proxy (10 minutes)
Coolify includes built-in Traefik for automatic SSL via Let’s Encrypt. Configure your domain’s DNS to point to your VPS IP, then in Coolify, each application gets automatic HTTPS.
Phase 4: Core Services (2 hours)
Gitea — Code Hosting
Via Coolify: New Service → Docker Compose → paste Gitea compose. Or directly:
docker run -d --name gitea \
-p 3000:3000 \
-v gitea-data:/data \
gitea/gitea:latest
Configure domain in Coolify for HTTPS. Import existing repos or start fresh.
Woodpecker CI — Continuous Integration
Connect Woodpecker to your Gitea instance. Create .woodpecker.yml in any repo to define CI pipelines.
Vaultwarden — Password Management
docker run -d --name vaultwarden \
-v vw-data:/data \
-e ADMIN_TOKEN=$(openssl rand -base64 48) \
vaultwarden/server:latest
Uptime Kuma — Monitoring
docker run -d --name uptime-kuma \
-p 3001:3001 \
-v uptime-kuma:/app/data \
louislam/uptime-kuma:latest
Umami — Analytics
Simple Docker Compose with PostgreSQL. Point at your website, configure domain.
Phase 5: Backups (30 minutes)
Install Restic and configure automated backups:
apt install restic -y
restic -r sftp://user@backup-server:/backups init
restic -r sftp://user@backup-server:/backups backup \
/var/lib/docker/volumes/gitea-data \
/var/lib/docker/volumes/vw-data \
/etc
echo "0 2 * * * /usr/local/bin/backup.sh" | crontab -
Phase 6: Optional Extras
Nextcloud — if you need file sync, calendar, contacts. Resource-heavy but comprehensive. Allocate at least 2 GB RAM dedicated.
Matrix — if you need self-hosted chat. Start with Conduit (lightweight Rust homeserver) instead of Synapse to test whether you actually need it.
Jellyfin — if you’re hosting media. Lightweight, good performance even on modest VPS.
The Maintenance Schedule
Weekly (15 minutes):
- Check Uptime Kuma dashboard for any alerts
- Review system logs:
journalctl -p err --since "1 week ago"
Monthly (30 minutes):
apt update && apt upgrade- Pull latest Docker images
- Check backup logs — did the last week of backups succeed?
- Review disk usage:
df -h
Quarterly (1 hour):
- Test restore from backup (critical!)
- Review and rotate SSH keys
- Review fail2ban logs for blocked IPs
- Update Coolify to latest version
You Now Have
A complete FOSS infrastructure stack running on your own hardware (virtual or physical), costing €6-9/month, with:
- Code hosting (Gitea) — your GitHub replacement
- CI/CD (Woodpecker) — your GitHub Actions replacement
- Password management (Vaultwarden) — your 1Password replacement
- Monitoring (Uptime Kuma) — know when things break
- Analytics (Umami) — know your visitors without tracking them
- Backups (Restic) — sleep at night
- Deployment (Coolify) — deploy like it’s Heroku
All FOSS. All self-hosted. All under your control.