Home server Docker Compose stack
Find a file
padriano b896bfcb79 docs: document static host IP and DHCP pool for external access
Record the final networking setup: host static 192.168.1.2 (netplan),
HA/Samba macvlan .246/.247, DHCP pool narrowed to .11-.245 so the
statics stay protected, and why static-at-host beats DHCP reservation
on the NOS router. Hard-code 192.168.1.2 in the port-forward table.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 21:46:45 +01:00
caddy add Caddy HTTPS reverse proxy with Let's Encrypt via DuckDNS DNS-01 2026-05-11 22:30:47 +01:00
plans retarget HA upgrade plan to 2026.5.4 (latest 2026.5 patch) 2026-06-04 14:41:42 +01:00
samba fix samba write permissions and document UID requirement 2026-05-09 22:34:52 +01:00
timemachine Initial commit 2026-05-09 15:45:56 +01:00
.env.example add samba SMB share container on macvlan network 2026-05-09 21:45:01 +01:00
.gitignore Initial commit 2026-05-09 15:45:56 +01:00
docker-compose.yml upgrade Home Assistant 2024.5.4 → 2026.5.4 2026-06-04 15:52:21 +01:00
README.md docs: document static host IP and DHCP pool for external access 2026-06-22 21:46:45 +01:00

Docker Compose — Home Server

Services

Container Image Purpose
duckdns lscr.io/linuxserver/duckdns Dynamic DNS — keeps *.duckdns.org pointed at the home IP
caddy caddy (custom build) HTTPS reverse proxy with automatic Let's Encrypt certificates via DuckDNS DNS-01
homeassistant homeassistant/home-assistant Home automation, reachable at https://ha-padriano.duckdns.org
timemachine mbentley/timemachine:smb Time Machine backup server over SMB
samba dperson/samba General SMB file share for Mac and Linux clients
forgejo codeberg.org/forgejo/forgejo Self-hosted Git server at https://git-padriano.duckdns.org, SSH at :2222

Initial Setup

  1. Copy and fill in the environment file:

    cp .env.example .env
    nano .env
    
  2. Register the required DuckDNS subdomains at duckdns.org: ha-padriano, padrianoha, padriano, git-padriano.

  3. Create the required host directories if they don't exist:

    mkdir -p /home/padriano/projects/forgejo
    mkdir -p /home/padriano/share
    

    The Samba container runs as UID/GID 1001 internally, so the share directory must be owned by that user:

    sudo chown 1001:1001 /home/padriano/share
    
  4. Add the reverse proxy trusted header config to Home Assistant:

    sudo tee -a /home/padriano/projects/ha/config/configuration.yaml > /dev/null << 'EOF'
    
    http:
      use_x_forwarded_for: true
      trusted_proxies:
        - 172.16.0.0/12
    EOF
    
  5. Bring the full stack up (first run builds the custom Caddy image):

    cd /home/padriano/projects/composer
    docker compose up -d --build
    

Common Docker Compose Commands

Stack management

# Start all services (detached)
docker compose up -d

# Stop all services (containers remain)
docker compose stop

# Stop and remove all containers (data volumes are preserved)
docker compose down

# Restart all services
docker compose restart

# Restart a single service
docker compose restart homeassistant

Viewing status and logs

# Show running containers and their status
docker compose ps

# Follow logs for all services
docker compose logs -f

# Follow logs for a single service
docker compose logs -f forgejo

# Show last 50 lines for a service
docker compose logs --tail=50 timemachine

Updating images

# Pull latest images for all services
docker compose pull

# Pull for a single service
docker compose pull forgejo

# Recreate containers that have a newer image
docker compose up -d --pull always

# Recreate a single service after pulling
docker compose up -d --force-recreate forgejo

Inspecting containers

# Open a shell inside a running container
docker exec -it homeassistant bash
docker exec -it forgejo sh        # alpine-based, use sh not bash

# Show resource usage (CPU, RAM, network)
docker stats

# Inspect container configuration
docker inspect forgejo

Service Notes

duckdns

  • Config stored at /home/padriano/projects/duckdns/config
  • Domains managed: ha-padriano, padrianoha, padriano, git-padriano (all .duckdns.org)
  • Subdomains must be registered manually at duckdns.org before they can be updated

caddy

  • Custom image built from caddy/Dockerfile — adds the DuckDNS DNS provider plugin to the official Caddy image
  • Config at caddy/Caddyfile; TLS certificates stored in the caddy_data Docker volume
  • Obtains free Let's Encrypt certificates via DNS-01 challenge (no extra ports needed, no cost)
  • Listens on :80 (redirects to HTTPS) and :443
  • Routes:
    • ha-padriano.duckdns.org → Home Assistant :8123
    • git-padriano.duckdns.org → Forgejo :3000
  • To reload config without restarting: docker exec caddy caddy reload --config /etc/caddy/Caddyfile

homeassistant

  • Config stored at /home/padriano/projects/ha/config
  • Web UI: https://ha-padriano.duckdns.org (via Caddy) or http://server:8123 on the LAN
  • Runs on the internal bridge network so Caddy can reach it by container name
  • configuration.yaml must include http.use_x_forwarded_for: true and trusted_proxies for the Docker bridge range so HA accepts forwarded headers from Caddy
  • Has a healthcheck — status shows (health: starting) for ~60s on first boot

timemachine

  • Backup data stored at /home/padriano/backup/timemachine
  • Uses network_mode: host and a fixed hostname: timemachine — the stable hostname prevents macOS from losing the backup destination after container restarts
  • On your Mac: System Settings → General → Time Machine → Add Backup Disk

samba

  • Shared directory on the host: /home/padriano/share — must be owned by 1001:1001 (the UID/GID the container uses internally); without this, the share mounts on the Mac but files cannot be written
  • Uses a macvlan network with static IP 192.168.1.247 — avoids port 445 conflict with the timemachine container
  • Config stored at samba/smb.conf; the [Share] section is the only exported share
  • Avahi service file at samba/avahi-smb.service advertises the share for Finder sidebar discovery (installed to /etc/avahi/services/samba.service on the host)
  • Connecting from a Mac: open Finder → Go → Connect to Server → smb://192.168.1.247/Share
  • Connecting from Linux: smbclient //192.168.1.247/Share -U samba or mount with mount -t cifs //192.168.1.247/Share /mnt/share -o username=samba
  • Username and password are set via SAMBA_USERNAME / SAMBA_PASSWORD in .env

forgejo

  • All data (repos, config, SQLite DB) stored at /home/padriano/projects/forgejo
  • Backing up this single directory is sufficient for a full restore
  • Port 3000 is no longer exposed on the host — access is via Caddy only

First-run setup

Visit http://server:3000 on first start (LAN only) and complete the install wizard:

  • Base URL: https://git-padriano.duckdns.org
  • SSH domain: git-padriano.duckdns.org
  • SSH port: 2222

Access

Method Local network External (via DuckDNS)
Web UI http://server:3000 https://git-padriano.duckdns.org
Git SSH ssh://git@server:2222 ssh://git@git-padriano.duckdns.org:2222

For external access, forward these ports on your router to the server IP — see Networking & External Access below for the full setup.

Git remote URL examples

# Add remote via HTTPS
git remote add origin https://git-padriano.duckdns.org/padriano/my-repo.git

# Add remote via SSH
git remote add origin ssh://git@git-padriano.duckdns.org:2222/padriano/my-repo.git

# Clone via SSH (local)
git clone ssh://git@server:2222/padriano/my-repo.git

Networking & External Access

Address layout

The stack uses fixed addresses on the 192.168.1.0/23 LAN:

Address Belongs to Assigned by
192.168.1.2 The server host itself (Docker publishes Caddy :443/:80 and Forgejo :2222 here) Static at the OS level — netplan /etc/netplan/99-server-static.yaml on interface enp0s31f6
192.168.1.246 homeassistant macvlan interface Static (docker-compose.yml)
192.168.1.247 samba macvlan interface Static (docker-compose.yml)

Caddy and Forgejo publish their ports on the host IP (192.168.1.2, via Docker port mapping) — not on 192.168.1.246. The macvlan IP belongs only to the Home Assistant container; Caddy reaches HA over the internal bridge network by container name.

Keeping these IPs stable (router DHCP)

All three static IPs must sit outside the router's DHCP pool so the router can never lease them to another device. On the NOS F@st 5670 the DHCP pool is set to:

  • Pool start 192.168.1.11, end 192.168.1.245 — this leaves .2.10 and .246.254 free for static use.

Why static-at-the-host instead of a DHCP reservation: the NOS router only reserves a device's current lease, and refuses to reserve any IP already present in the DHCP leases table. Combined with the macvlan containers (which don't use DHCP at all and never appear in the leases table), reservations proved unworkable. Pinning the host IP in netplan and carving the static range out of the DHCP pool is the reliable approach.

History: the host originally took 192.168.1.246 from DHCP — the same IP statically assigned to the Home Assistant container — causing an address collision (two MACs answering for .246). Moving the host to a static .2 outside the pool resolved it.

To re-apply the host static IP after a fresh OS install, recreate /etc/netplan/99-server-static.yaml:

network:
  version: 2
  renderer: NetworkManager
  ethernets:
    enp0s31f6:
      dhcp4: false
      addresses:
        - 192.168.1.2/23
      routes:
        - to: default
          via: 192.168.1.1
      nameservers:
        addresses: [192.168.1.1, 1.1.1.1]

Then sudo netplan apply. Cloud-init network management is disabled via /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg (network: {config: disabled}) so it doesn't override this on reboot.

Router port forwarding

Forward exactly two ports to the host IP 192.168.1.2. The single 443 rule serves both sites — Caddy routes by hostname.

Service Protocol External port Internal host Internal port
HTTPS (Caddy → Home Assistant and Forgejo) TCP 443 192.168.1.2 443
Forgejo SSH TCP 2222 192.168.1.2 2222

Do not forward:

  • 8123 (Home Assistant) — bypasses Caddy and exposes the UI over plaintext HTTP. Use https://ha-padriano.duckdns.org instead.
  • 3000 (Forgejo) — not exposed on the host; access is via Caddy only.
  • 22 — not used by this stack; Forgejo SSH is on 2222.

Port 80 is optional — forward it only if you want bare http:// requests auto-redirected to HTTPS. It is not required for certificates, which use the DuckDNS DNS-01 challenge.


Disk Space

# Free space on the main filesystem
df -h /

# Free space on all mounted filesystems
df -h

# Space used by each service's data directory
du -sh /home/padriano/projects/* /home/padriano/backup/*

Data & Backup Summary

Service Host path Notes
duckdns /home/padriano/projects/duckdns/config Token cache, not critical
caddy Docker volume caddy_data Issued TLS certificates — auto-reissued if lost
homeassistant /home/padriano/projects/ha/config All HA config and automations
timemachine /home/padriano/backup/timemachine Mac backup sparsebundles
samba /home/padriano/share General file share — back up as needed
forgejo /home/padriano/projects/forgejo All repos, DB, config