Run self-hosted services on Bluefin DX using Podman Quadlets and linuxserver.io containers — persistent, auto-updating, systemd-managed.
podman run directly (see podman skill)Bluefin DX is an excellent homelab host:
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)# Get your PUID and PGID
id -u # e.g., 1000
id -g # e.g., 1000
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 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
# Activate
systemctl --user daemon-reload
systemctl --user start jellyfin
systemctl --user enable jellyfin # start on login
systemctl --user status jellyfin
journalctl --user -u jellyfin -f # logs
Jellyfin (media server)
Image=lscr.io/linuxserver/jellyfin:latest
PublishPort=8096:8096
# Optional hardware acceleration (Intel):
# AddDevice=/dev/dri/renderD128
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
# Network mode host recommended for device discovery:
# Network=host
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
}
Ports < 1024 (rootless constraint): Rootless containers cannot bind to ports below 1024. Use port remapping (e.g., 8080:80) and a reverse proxy, or:
# Allow rootless binding to port 80+ if needed
sudo sysctl net.ipv4.ip_unprivileged_port_start=80
# Make persistent:
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
# Services are accessible at your Tailscale IP — no port forwarding needed
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
# List named volumes
podman volume ls
# Inspect a volume (find actual path)
podman volume inspect myservice_db
Create bind mount directories before starting the service:
mkdir -p ~/jellyfin/config ~/media
Enable auto-update for all Quadlet services that have AutoUpdate=registry:
# Enable the systemd timer
systemctl --user enable --now podman-auto-update.timer
# Check timer status
systemctl --user status podman-auto-update.timer
# Run manually
podman auto-update --dry-run # preview
podman auto-update # apply
Restic to a local NAS or cloud:
brew install restic
# Initialize repository
restic -r sftp:user@nas:/backups/jellyfin init
# Backup config
restic -r sftp:user@nas:/backups/jellyfin backup ~/jellyfin/config
# List snapshots
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
Container can't bind to port:
ss -tlnp | grep :8096ip_unprivileged_port_startPermission denied on bind mount:
ls -la ~/myservice/chown -R 1000:1000 ~/myservice/:z is on the volume mountService not starting on login (Quadlet):
systemctl --user enable myservice # must be enabled, not just started
loginctl enable-linger $USER # start user services at boot (without login)
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.
| 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 |