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

Related