I need some help if anyone could take the time and has the knowledge:
I’m basically new to podman and namespaces, relatively new to linux and a noob at networking. So figuring this out and getting it to work took many more hours than I would like to admit, but I still have a few problems. I have all my current Quadlets below in the spoiler (seperated by “—”, assume user123 = UID 1000). I am on Bazzite, rootless Podman, which probably makes this even harder.
Spoiler with the Quadlets
[Unit]
Description=Arr-stack pod
[Pod]
PodName=arr-stack
# Network
# Network=vpn-only
# User mapping / I don't fully understand this yet, but the pod does not work without this (maps user id to specified ID inside the containers? So the containers have UID:GID 1000:1000?)
UserNS=keep-id:uid=1000,gid=1000
#
# Homepage Port Mapping
PublishPort=3000:3000
# Jellyfin Port Mapping
PublishPort=8096:8096/tcp
# qBittorrent Port Mapping
PublishPort=8080:8080
#PublishPort=6881:6881
#PublishPort=6881:6881/udp
# Prowlarr Port Mapping
PublishPort=9696:9696
# Flaresolverr Port Mapping
PublishPort=8191:8191
# Radarr Port Mapping
PublishPort=7878:7878
# Sonarr Port Mapping
PublishPort=8989:8989
# Jellyseerr Port Mapping
#PublishPort=8055:5055
#[Install]
# WantedBy=default.target
---
[Unit]
Description=Gluetun Container
# Dependencies
# pod
Wants=arr-stack-pod.service
After=arr-stack-pod.service
Requires=arr-stack-pod.service
PartOf=arr-stack-pod.service
# .pod is probably not quite what I want, but it works and I might as well keep it, in case they change the syntax
Wants=arr-stack.pod
After=arr-stack.pod
Requires=arr-stack.pod
PartOf=arr-stack.pod
[Container]
ContainerName=gluetun
Pod=arr-stack.pod
Image=docker.io/qmcgaw/gluetun:v3
AutoUpdate=registry
# Network
# Network=vpn-only
# UID/GID permissions / root + privileged for networking?
PodmanArgs=--privileged
User=0
Group=0
# Equivalent to cap_add: - NET_ADMIN # one wrong?
AddCapability=NET_ADMIN
AddCapability=CAP_NET_ADMIN
# Required for Gluetun to delete the bridge's default route, but does not work
AddCapability=NET_RAW
AddCapability=CAP_NET_RAW
# Equivalent to "devices: - /dev/net/tun:/dev/net/tun"
AddDevice=/dev/net/tun:/dev/net/tun
# EnvironmentFile=global.env
Timezone=UTC
Environment=TZ=Etc/UTC
# EnvironmentFile=gluetun.env
# Environment=FIREWALL_OUTBOUND_SUBNETS=10.90.0.0/24 / test from a specific podman network
Environment=FIREWALL_INPUT_PORTS=8080
#
Environment=VPN_SERVICE_PROVIDER= <123>
Environment=VPN_TYPE=wireguard
Environment=WIREGUARD_PRIVATE_KEY= <key>
Environment=SERVER_COUNTRIES= <country>
# for now:
Environment=VPN_PORT_FORWARDING=off
#Secret=openvpn_user,type=env,target=OPENVPN_USER
#Secret=openvpn_password,type=env,target=OPENVPN_PASSWORD
#Volume
Volume=/var/home/user123/.config/arr-configs/gluetun:/gluetun:Z
# SecurityLabel=disable
[Service]
Restart=always
#[Install]
#WantedBy=default.target
---
[Unit]
Description=qBittorrent Container
# Dependencies
# pod
Wants=arr-stack-pod.service
After=arr-stack-pod.service
Requires=arr-stack-pod.service
PartOf=arr-stack-pod.service
# .pod is probably not quite what I want, but it works and I might as well keep it, in case they change the syntax
Wants=arr-stack.pod
After=arr-stack.pod
Requires=arr-stack.pod
PartOf=arr-stack.pod
# gluetun
Wants=gluetun.service
After=gluetun.service
Requires=gluetun.service
BindsTo=gluetun.service
# .container is probably not quite what I want, but it works and I might as well keep it, in case they change the syntax
Wants=gluetun.container
After=gluetun.container
Requires=gluetun.container
BindsTo=gluetun.container
[Container]
ContainerName=qbittorrent
Pod=arr-stack.pod
Image=lscr.io/linuxserver/qbittorrent:latest
AutoUpdate=registry
# Network
Network=container:gluetun
# UID/GID permissions / linuxserver images require UID:GID 0:0 at the start; they won't start without it
User=0
Group=0
Environment=PUID=1000
Environment=PGID=1000
# EnvironmentFile=global.env
Timezone=UTC
Environment=TZ=Etc/UTC
# EnvironmentFile=qbittorrent.env
Environment=WEBUI_PORT=8080
# Environtment=TORRENTING_PORT=6881
# Volume :Z (> :z) probably works as well and is saver for configs?
Volume=/var/home/user123/.config/arr-configs/qbittorrent:/config:z
Volume=/var/home/user123/Videos/Downloads:/downloads:z
# Volume=/var/home/user123/Videos/Downloads/completed:/downloads:z,U
# Volume=/var/home/user123/Videos/Downloads/incomplete:/incomplete:z,U
# Volume=/var/home/user123/Videos/Downloads/torrents:/torrents:z,U
[Service]
Restart=always
#[Install]
#WantedBy=default.target
---
[Unit]
Description=Prowlarr Container
# Dependencies
# pod
Wants=arr-stack-pod.service
After=arr-stack-pod.service
Requires=arr-stack-pod.service
PartOf=arr-stack-pod.service
# .pod is probably not quite what I want, but it works and I might as well keep it, in case they change the syntax
Wants=arr-stack.pod
After=arr-stack.pod
Requires=arr-stack.pod
PartOf=arr-stack.pod
# gluetun
Wants=gluetun.service
After=gluetun.service
Requires=gluetun.service
BindsTo=gluetun.service
# .container is probably not quite what I want, but it works and I might as well keep it, in case they change the syntax
Wants=gluetun.container
After=gluetun.container
Requires=gluetun.container
BindsTo=gluetun.container
[Container]
ContainerName=prowlarr
Pod=arr-stack.pod
Image=lscr.io/linuxserver/prowlarr:latest
AutoUpdate=registry
# Network
Network=container:gluetun
# UID/GID permissions / linuxserver images require UID:GID 0:0 at the start; they won't start without it
User=0
Group=0
Environment=PUID=1000
Environment=PGID=1000
# EnvironmentFile=global.env
Timezone=UTC
Environment=TZ=Etc/UTC
# EnvironmentFile=prowlarr.env
Environment=WEBUI_PORT=9696
# Volume
Volume=/var/home/user123/.config/arr-configs/prowlarr:/config:z,U
[Service]
Restart=always
#[Install]
#WantedBy=default.target
---
[Unit]
Description=Sonarr Container
# Dependencies
# pod
Wants=arr-stack-pod.service
After=arr-stack-pod.service
Requires=arr-stack-pod.service
PartOf=arr-stack-pod.service
# .pod is probably not quite what I want, but it works and I might as well keep it, in case they change the syntax
Wants=arr-stack.pod
After=arr-stack.pod
Requires=arr-stack.pod
PartOf=arr-stack.pod
# gluetun
Wants=gluetun.service
After=gluetun.service
Requires=gluetun.service
BindsTo=gluetun.service
# .container is probably not quite what I want, but it works and I might as well keep it, in case they change the syntax
Wants=gluetun.container
After=gluetun.container
Requires=gluetun.container
BindsTo=gluetun.container
[Container]
ContainerName=sonarr
Pod=arr-stack.pod
Image=lscr.io/linuxserver/sonarr:latest
AutoUpdate=registry
# Network
Network=container:gluetun
# UID/GID permissions / linuxserver images require UID:GID 0:0 at the start; they won't start without it
User=0
Group=0
Environment=PUID=1000
Environment=PGID=1000
# EnvironmentFile=global.env
Timezone=UTC
Environment=TZ=Etc/UTC
# EnvironmentFile=sonarr.env
Environment=WEBUI_PORT=8989
# Volume / Disable SecurityLabels due to SMB share, need to look this up
SecurityLabelDisable=true
Volume=/var/home/user123/.config/arr-configs/sonarr:/config:z
Volume=/var/home/user123/Videos/Shows:/tv:z
Volume=/var/home/user123/Videos/Downloads:/downloads:z
[Service]
Restart=always
#[Install]
#WantedBy=default.target
---
[Unit]
Description=Radarr Container
# Dependencies
# pod
Wants=arr-stack-pod.service
After=arr-stack-pod.service
Requires=arr-stack-pod.service
PartOf=arr-stack-pod.service
# .pod is probably not quite what I want, but it works and I might as well keep it, in case they change the syntax
Wants=arr-stack.pod
After=arr-stack.pod
Requires=arr-stack.pod
PartOf=arr-stack.pod
# gluetun
Wants=gluetun.service
After=gluetun.service
Requires=gluetun.service
BindsTo=gluetun.service
# .container is probably not quite what I want, but it works and I might as well keep it, in case they change the syntax
Wants=gluetun.container
After=gluetun.container
Requires=gluetun.container
BindsTo=gluetun.container
[Container]
ContainerName=radarr
Pod=arr-stack.pod
Image=lscr.io/linuxserver/radarr:latest
AutoUpdate=registry
# Network
Network=container:gluetun
# UID/GID permissions / linuxserver images require UID:GID 0:0 at the start; they won't start without it
User=0
Group=0
Environment=PUID=1000
Environment=PGID=1000
# EnvironmentFile=global.env
Timezone=UTC
Environment=TZ=Etc/UTC
# EnvironmentFile=radarr.env
Environment=WEBUI_PORT=7878
# Volume / Disable SecurityLabels due to SMB share
SecurityLabelDisable=true
Volume=/var/home/user123/.config/arr-configs/radarr:/config:z
Volume=/var/home/user123/Videos/Movies:/movies:z
Volume=/var/home/user123/Videos/Downloads:/downloads:z
[Service]
Restart=always
#[Install]
#WantedBy=default.target
---
[Unit]
Description=Flaresolverr Container
# Dependencies
# pod
Wants=arr-stack-pod.service
After=arr-stack-pod.service
Requires=arr-stack-pod.service
PartOf=arr-stack-pod.service
# .pod is probably not quite what I want, but it works and I might as well keep it, in case they change the syntax
Wants=arr-stack.pod
After=arr-stack.pod
Requires=arr-stack.pod
PartOf=arr-stack.pod
# gluetun
Wants=gluetun.service
After=gluetun.service
Requires=gluetun.service
BindsTo=gluetun.service
# .container is probably not quite what I want, but it works and I might as well keep it, in case they change the syntax
Wants=gluetun.container
After=gluetun.container
Requires=gluetun.container
BindsTo=gluetun.container
[Container]
ContainerName=flaresolverr
Pod=arr-stack.pod
Image=ghcr.io/flaresolverr/flaresolverr:latest
AutoUpdate=registry
# Network
Network=container:gluetun
# UID/GID permissions
User=0
Group=0
Environment=PUID=1000
Environment=PGID=1000
# EnvironmentFile=global.env
Timezone=UTC
Environment=TZ=Etc/UTC
# EnvironmentFile=flaresolverr.env
Environment=WEBUI_PORT=8191
Environment=LOG_LEVEL=info
Environment=LOG_HTML=false
Environment=CAPTCHA_SOLVER=none
# Volume=flaresolverr:/app/
[Service]
Restart=always
#[Install]
#WantedBy=default.target
---
[Unit]
Description=Podman - Jellyfin
# Dependencies
# pod
Wants=arr-stack-pod.service
After=arr-stack-pod.service
Requires=arr-stack-pod.service
PartOf=arr-stack-pod.service
# .pod is probably not quite what I want, but it works and I might as well keep it, in case they change the syntax
Wants=arr-stack.pod
After=arr-stack.pod
Requires=arr-stack.pod
PartOf=arr-stack.pod
# gluetun
Wants=gluetun.service
After=gluetun.service
Requires=gluetun.service
BindsTo=gluetun.service
# .container is probably not quite what I want, but it works and I might as well keep it, in case they change the syntax
Wants=gluetun.container
After=gluetun.container
Requires=gluetun.container
BindsTo=gluetun.container
[Container]
ContainerName=jellyfin
Pod=arr-stack.pod
Image=ghcr.io/jellyfin/jellyfin
AutoUpdate=registry
# Network
Network=container:gluetun
# UID/GID permissions / 1000:1000 might work?
User=0
Group=0
Environment=PUID=1000
Environment=PGID=1000
# EnvironmentFile=global.env
Timezone=UTC
Environment=TZ=Etc/UTC
# EnvironmentFile=jellyfin.env
Environment=WEBUI_PORT=8096:8096/tcp
#PublishPort=8096:8096/tcp
#PublishPort=8920:8920
#PublishPort=7359:7359/udp
#PublishPort=1900:1900/udp
# Volume
Volume=/var/home/user123/.config/arr-configs/jellyfin:/config:z
Volume=/var/home/user123/Videos/jellyfin-cache:/cache:z
Volume=/var/home/user123/Videos/Movies:/data/movies:z
Volume=/var/home/user123/Videos/Shows:/data/shows:z
[Service]
# Inform systemd of additional exit status
# SuccessExitStatus=0 143a
Restart=always
TimeoutStartSec=900
#[Install]
# Start by default on boot
#WantedBy=default.target
---
[Unit]
Description=Homepage Dashboard
# Dependencies
# pod
Wants=arr-stack-pod.service
After=arr-stack-pod.service
Requires=arr-stack-pod.service
PartOf=arr-stack-pod.service
# .pod is probably not quite what I want, but it works and I might as well keep it, in case they change the syntax
Wants=arr-stack.pod
After=arr-stack.pod
Requires=arr-stack.pod
PartOf=arr-stack.pod
# gluetun
Wants=gluetun.service
After=gluetun.service
Requires=gluetun.service
BindsTo=gluetun.service
# .container is probably not quite what I want, but it works and I might as well keep it, in case they change the syntax
Wants=gluetun.container
After=gluetun.container
Requires=gluetun.container
BindsTo=gluetun.container
# idk about this?:
After=network-online.target
Wants=network-online.target
# Socket
Wants=podman.socket
After=podman.socket
Requires=podman.socket
[Container]
ContainerName=homepage
Pod=arr-stack.pod
Image=ghcr.io/gethomepage/homepage:latest
AutoUpdate=registry
# Network
Network=container:gluetun
# UID/GID permissions
User=1000
Group=1000
Environment=PUID=1000
Environment=PGID=1000
# EnvironmentFile=global.env
Timezone=UTC
Environment=TZ=Etc/UTC
# EnvirontmentFile=homepage.env
#Environment=LOG_LEVEL=debug
Environment=HOMEPAGE_ALLOWED_HOSTS=gethomepage.dev
#PublishPort=3000:3000
# Podman socket (recommended on Bazzite)
Volume=%t/podman/podman.sock:/var/run/docker.sock:ro
#Volume=/var/run/docker.sock:/run/user/1000/podman/podman.sock:ro
#Volume=/%t/podman/podman.sock:/run/user/1000/podman/podman.sock:ro
# Volume / Config directory
SecurityLabelDisable=true
Volume=%h/apps/homepage:/app/config:Z
Volume=%h/apps/homepage/icons:/app/public/icons:Z
[Service]
Restart=on-failure
TimeoutStartSec=300
#[Install]
#WantedBy=default.target
Questions:
-
- If I use “podman exec <container> ip route” (on e.g. qbittorrent) the default route goes through my actual network interface (actual ip adress) which I very much do not want (or through my killswitch dummy network from my VPN if on, which is better but still not ideal). Is there a way to completely remove my actual network from a container’s eyes? “podman exec <container> ip addr” shows 1 lo (local), 2 my actual network, 4 tun0 from gluetun. The traffic does go through gluetun correctly, but I don’t trust it 100%. Having the containers separated and NOT inside a pod gives the same result, since the containers share the network namespaces from the gluetun container when I do “Network=container:gluetun” (same as just having them in a pod as far as I understand). I tried to also create a podman network without a default gateway, but then gluetun cannot connect to the VPN in the first place.
-
- My setup works but is quite convoluted and probably has many unnecessary lines, so please give me any improvements you see
-
- Is User=1000, Group=1000, even sensible? For example in the homepage container those lines result in the container showing User “1000:1000” (from podman inspect). Would User=0, Group=0 (or no lines since I use UserNS=keep-id in the pod?), which shows as User=root (podman inspect) mean that it has actual root access or just that it is root INSIDE the container?
Thank you in advance for the answers, in case I don’t reply to your comment specifically.


@Excaliburr Yes, you are right.
Do you have already a single container running with rootless podman?
Your given Arr-Stack seems to me relative complex when you are a beginner with podman.
Probably you could get some experience, when you start with just a single container.
In following posts are my personal quadlet settings for running Portainer, which could be helpful for getting insights of your running containers.
https://docs.portainer.io/start/install-ce/server/podman/linux
I’ve been running Docker for various things on Windows before and I’m not a stranger to computers and tech in general, so that’s why I went this deep from the get go. The whole stack works and I’m like 90% sure that it works the way I want it to work, I just want to make it better (and “bullet proof”). I don’t have much experience with networking or Linux, though, so yeah, this was quite the challenge haha. Thanks again for the help, I’ll have a look at your quadlets.
[Unit] Description=Container running Portainer container management platform Wants=podman-user-wait-network-online.service After=podman-user-wait-network-online.service After=local-fs.target Requires=podman.socket[Container] AutoUpdate=registry ContainerName=con-portainer HostName=con-portainer Image=docker.io/portainer/portainer-ce:lts Notify=conmon PublishPort=8000:8000 PublishPort=9443:9443 Pull=missing Volume=vol-portainer:/data Volume=%t/podman/podman.sock:%t/podman/podman.sock[Install] \# Start by default on boot without explicit enabling \# To avoid automatic start Install section has to be \# disabled WantedBy=default.target multi-user.target [Service] \# Restart service in case of failures Restart=on-failure TimeoutStartSec=20 TimeoutStopSec=20 Type=notifyMaybe you have to check Volume and Port settings, so that it fit your needs.
I always prefix my containers with “con-”, so that it is easier to finding them with
systemctl --user.