| name | homelab |
| description | Run self-hosted services on Bluefin DX using Podman Quadlets and linuxserver.io containers — persistent, auto-updating, systemd-managed. |
| domain | cloud-native |
Homelab on Bluefin DX
When to Use
- Running self-hosted services (Jellyfin, Nextcloud, Home Assistant, etc.) on Bluefin DX
- Setting up persistent containers as systemd services (Quadlets)
- Using linuxserver.io container images with PUID/PGID/TZ patterns
- Managing homelab storage, networking, and backups on an immutable host
When NOT to Use
- Running containerized development environments — use Distrobox instead
- One-off container runs — use
podman run directly (see podman skill)
- Complex multi-host orchestration — use Kubernetes (see kubernetes skill)
Why Bluefin DX for Homelab
Bluefin DX is an excellent homelab host:
- Podman pre-installed — rootless containers without a daemon
- Quadlets — run containers as systemd units (auto-start, restart, journald logs)
- Immutable OS — host stays clean and stable across updates
- bootc rollback — instant OS rollback if an update breaks anything
- Tailscale built-in — secure remote access (see tailscale skill)
linuxserver.io Containers
linuxserver.io is the preferred source for homelab container images.
Every linuxserver image uses a consistent pattern:
Key environment variables:
PUID — run container processes as this user ID (use id -u to get yours)
PGID — run as this group ID (use id -g to get yours)
TZ — timezone (e.g., America/New_York)
id -u
id -g
Quick example — Jellyfin:
podman run -d \
--name jellyfin \
-e PUID=1000 \
-e PGID=1000 \
-e TZ=America/New_York \
-p 8096:8096 \
-v /home/jorge/jellyfin/config:/config \
-v /home/jorge/media:/data/media \
lscr.io/linuxserver/jellyfin:latest
Quadlets — Persistent Services
Quadlets are the recommended pattern for homelab services on Bluefin. Place .container files in ~/.config/containers/systemd/.
Jellyfin Quadlet (~/.config/containers/systemd/jellyfin.container):
[Unit]
Description=Jellyfin Media Server
After=network-online.target
[Container]
Image=lscr.io/linuxserver/jellyfin:latest
PublishPort=8096:8096
Volume=%h/jellyfin/config:/config:z
Volume=%h/media:/data/media:z
Environment=PUID=1000
Environment=PGID=1000
Environment=TZ=America/New_York
AutoUpdate=registry
[Service]
Restart=always
TimeoutStartSec=300
[Install]
WantedBy=default.target
systemctl --user daemon-reload
systemctl --user start jellyfin
systemctl --user enable jellyfin
systemctl --user status jellyfin
journalctl --user -u jellyfin -f
Common Homelab Services
Jellyfin (media server)
Image=lscr.io/linuxserver/jellyfin:latest
PublishPort=8096:8096
Nextcloud (self-hosted cloud storage)
Image=lscr.io/linuxserver/nextcloud:latest
PublishPort=443:443
Volume=%h/nextcloud/config:/config:z
Volume=%h/nextcloud/data:/data:z
Vaultwarden (Bitwarden-compatible password manager)
Image=docker.io/vaultwarden/server:latest
PublishPort=8080:80
Volume=%h/vaultwarden/data:/data:z
Environment=WEBSOCKET_ENABLED=true
Home Assistant (home automation)
Image=ghcr.io/home-assistant/home-assistant:stable
PublishPort=8123:8123
Volume=%h/homeassistant/config:/config:z
Environment=TZ=America/New_York
Caddy (reverse proxy with auto-HTTPS)
Image=docker.io/library/caddy:latest
PublishPort=80:80
PublishPort=443:443
Volume=%h/caddy/Caddyfile:/etc/caddy/Caddyfile:z
Volume=caddy_data:/data
Volume=caddy_config:/config
Caddyfile example:
jellyfin.home.example.com {
reverse_proxy localhost:8096
}
nextcloud.home.example.com {
reverse_proxy localhost:443
}
Networking
Ports < 1024 (rootless constraint):
Rootless containers cannot bind to ports below 1024. Use port remapping (e.g., 8080:80) and a reverse proxy, or:
sudo sysctl net.ipv4.ip_unprivileged_port_start=80
echo "net.ipv4.ip_unprivileged_port_start=80" | sudo tee /etc/sysctl.d/99-rootless-ports.conf
Tailscale for remote access (pre-installed on Bluefin):
sudo tailscale up
Storage — Persistent Data
Bind mounts (recommended for config/data you want to access directly):
Volume=%h/myservice/config:/config:z
Volume=%h/myservice/data:/data:z
Named volumes (managed by Podman, good for opaque data like databases):
Volume=myservice_db:/var/lib/database
podman volume ls
podman volume inspect myservice_db
Create bind mount directories before starting the service:
mkdir -p ~/jellyfin/config ~/media
Auto-Update
Enable auto-update for all Quadlet services that have AutoUpdate=registry:
systemctl --user enable --now podman-auto-update.timer
systemctl --user status podman-auto-update.timer
podman auto-update --dry-run
podman auto-update
Backup Strategies
Restic to a local NAS or cloud:
brew install restic
restic -r sftp:user@nas:/backups/jellyfin init
restic -r sftp:user@nas:/backups/jellyfin backup ~/jellyfin/config
restic -r sftp:user@nas:/backups/jellyfin snapshots
Best practice: Stop the container before backing up database files:
systemctl --user stop myservice
restic backup ~/myservice/data
systemctl --user start myservice
Troubleshooting
Container can't bind to port:
- Check if another process uses the port:
ss -tlnp | grep :8096
- Rootless can't bind <1024 — use higher port or adjust
ip_unprivileged_port_start
Permission denied on bind mount:
- Check PUID/PGID match directory owner:
ls -la ~/myservice/
- Fix ownership:
chown -R 1000:1000 ~/myservice/
- Ensure SELinux label
:z is on the volume mount
Service not starting on login (Quadlet):
systemctl --user enable myservice
loginctl enable-linger $USER
Enable linger for boot-time services:
sudo loginctl enable-linger $USER
This allows your user's systemd services to start at boot, before you log in.
Decision Guide
| Scenario | Tool |
|---|
| Run a self-hosted service persistently | Podman Quadlet |
| Pull linuxserver.io images | Podman + Quadlet |
| Reverse proxy + HTTPS | Caddy (in Quadlet) |
| Remote access without port forwarding | Tailscale |
| NAS / file storage | ZFS skill (external) or bind mounts |
| Complex multi-service orchestration | Kubernetes skill |