End-to-end guide for adding a new service to the homelab.
When to use
Adding a new Docker-based service that will run in its own LXC container.
Prerequisites
- [ ] Proxmox host access
- [ ] CT 902 (lxc-base template) exists and is configured
- [ ] Service has a Docker Compose configuration
- [ ] Know the service's resource requirements (CPU, RAM)
- [ ] Have an IP address available (check network topology)
- [ ] Checked the Docker image's documentation for: required env vars, default database type (SQLite vs PostgreSQL), CORS behaviour, and any DB_TYPE or similar selectors
Steps
1. Create the service configuration
In ~/dev/homelab-docker/ on your local machine:
mkdir <service>
cd <service>
Create docker-compose.yml:
version: "3"
services:
app:
image: image-name:latest
container_name: <service>
restart: unless-stopped
volumes:
- /data/<subdir>:/path/in/container
ports:
- "port:port"
environment:
- VAR=value
Create .env.example with environment variable templates (no secrets).
Create install.sh:
#!/bin/bash
set -e
# Validate required variables
if [ ! -f .env ]; then
echo "Error: .env file not found"
echo "Create .env from .env.example"
exit 1
fi
# Create data directories
mkdir -p /data/<subdirs>
# Start service
docker compose up -d
Create README.md documenting the service (CT, IP, ports, access).
2. Create LXC container
On Proxmox host:
# Clone template
pct clone 902 <NEW_ID> --hostname <service> --full
# Set resources (adjust as needed)
pct set <NEW_ID> --cores 2 --memory 2048
# Configure network
pct set <NEW_ID> -net0 name=eth0,bridge=vmbr0,firewall=1,gw=192.168.144.1,ip=<IP>/23
# Mount data directory
pct set <NEW_ID> -mp0 /lxcdata/<service>,mp=/data
# Mount homelab-docker configs (shared from host)
pct set <NEW_ID> -mp1 /home/jan/homelab-docker,mp=/opt/homelab-docker
# Enable features
pct set <NEW_ID> -features nesting=1,keyctl=1
# Auto-start on boot
pct set <NEW_ID> -onboot 1
3. Add AppArmor workaround
Required for Docker in LXC (CVE-2025-52881):
cat >> /etc/pve/lxc/<NEW_ID>.conf << 'EOF'
lxc.apparmor.profile: unconfined
lxc.mount.entry: /dev/null sys/module/apparmor/parameters/enabled none bind 0 0
EOF
4. Create data directory
mkdir -p /lxcdata/<service>
5. Start and deploy
# Start container
pct start <NEW_ID>
# Start Docker
pct exec <NEW_ID> -- bash -c 'systemctl enable --now docker'
# Configs are already available via bind mount
# Create .env file if needed
pct exec <NEW_ID> -- bash -c 'cp /opt/homelab-docker/<service>/.env.example /opt/homelab-docker/<service>/.env'
pct exec <NEW_ID> -- nano /opt/homelab-docker/<service>/.env
# Deploy service
pct exec <NEW_ID> -- bash -c 'cd /opt/homelab-docker/<service> && chmod +x install.sh && ./install.sh'
6. Configure external access (if needed)
For external access: Add route in Cloudflare Zero Trust dashboard
For internal HTTPS: Add to Lanproxy Caddyfile:
your-service.janvv.nl {
reverse_proxy <service-ip>:<port>
}
Then reload Lanproxy:
pct exec 127 -- bash -c 'cd /opt/homelab-docker/lanproxy && docker compose restart'
Verification
- [ ] Container is running:
pct status <NEW_ID>shows "running" - [ ] Docker containers running:
pct exec <NEW_ID> -- docker ps - [ ] Service accessible:
curl http://<service-ip>:<port> - [ ] External route working (if configured)
Troubleshooting
Container won't start: Check troubleshooting guide Docker issues: Verify AppArmor workaround is applied Network issues: Check firewall rules and IP availability