User:Bai-Chiang/Rootless podman with LinuxServer.io nextcloud and SWAG images
This is my notes of setting up rootless podman with LinuxServer.io images.
In this guide, I will set up three containers, Nextcloud, PostgreSQL and Secure Web Application Gateway (SWAG).
The Nextcloud and PostgreSQL service will run as nextcloud
user.
SWAG container, handling SSL certificate and reverse proxy, will run as a separate user swag
.
Installation
Install the podman and fuse-overlayfs packages.
If using linux-hardened kernel, create
/etc/sysctl.d/unprivileged_user_namespace.conf
kernel.unprivileged_userns_clone=1
to enable kernel.unprivileged_userns_clone
then reboot machine to apply the change.
Configuration
Create podman users
# useradd -m swag # useradd -m nextcloud
Enable lingering
# loginctl enable-linger swag # loginctl enable-linger nextcloud
Set subuid and subgid
- This section no longer needed. After shadow
4.11.1-3
, new user will be added to/etc/subuid
and/etc/subgid
by default. - If you are using systemd-homed, the minimum UID and GID for containers must be at least 524288 (check the "begin container users" value in the output of
userdbctl
).
Add these lines to /etc/subuid
and /etc/subgid
.
/etc/subuid
swag:524288:65536 nextcloud:589824:65536
and
/etc/subgid
swag:524288:65536 nextcloud:589824:65536
This allocates uid and gid range 524288-589823
to swag
user, and 589824-655359
to nextcloud
user.
If these ranges are already taken by other users, you need to shift/adjust the ranges accordingly.
Then run
$ podman system migrate
to propagate changes to subuid and subgid
Allow rootless podman access 443 port
Create file
/etc/sysctl.d/unprivileged_port_start.conf
net.ipv4.ip_unprivileged_port_start=443
Reboot system to apply change.
Firewall 443/tcp port
If using firewalld (default zone):
# firewall-cmd --permanent --add-port 443/tcp # firewall-cmd --reload
If using uncomplicated Firewall:
# ufw allow 443/tcp
Test podman
Fist get an interactive shell as swag
user, run
# machinectl shell swag@
Using machinectl
instead of sudo -u swag
so that it will start systemd user session.
Then run
$ podman run docker.io/library/hello-world
It should pull the docker.io/library/hello-world
image, and print
Hello from Docker! This message shows that your installation appears to be working correctly. ...
To exit the interactive shell, run
$ exit
or press Ctrl+[
three times within 1s.
Reverse proxy
Set up reverse proxy using LinuxServer SWAG image.
This tutorial will use mydomain.com
and Cloudflare DNS records as an example.
Secure web application gateway (SWAG)
Get an interactive shell as swag
user
# machinectl shell swag@
Create swag config directory
$ mkdir /home/swag/config
Run the container
$ podman run \ --rm \ --detach \ --replace \ --label io.containers.autoupdate=registry \ --uidmap 1000:0:1 \ --uidmap 0:1:1000 \ --uidmap 1001:1001:64536 \ --network=slirp4netns:allow_host_loopback=true,cidr=10.0.2.0/24,port_handler=slirp4netns \ --dns=1.1.1.1 \ --name=swag \ --cap-add=NET_ADMIN \ --env PUID=1000 \ --env PGID=1000 \ --env TZ=US/Eastern \ --publish 443:443 \ --volume /home/swag/config:/config:Z \ --env URL=mydomain.com \ --env VALIDATION=dns \ --env SUBDOMAINS=www,nextcloud,dashboard \ --env CERTPROVIDER=letsencrypt \ --env DNSPLUGIN=cloudflare \ --env EMAIL=mail@mydomain.com \ --env ONLY_SUBDOMAINS=true \ --env STAGING=false \ --env PROPAGATION=60 \ --env DOCKER_MODS='linuxserver/mods:swag-dashboard|linuxserver/mods:swag-auto-reload' \ lscr.io/linuxserver/swag:latest
--rm
will automatically remove the container when it exits.- If another container with the same name already exists,
--replace
will replace and remove it. - podman-auto-update(1) looks up containers with
--label io.containers.autoupdate=registry
. - The LinuxServer.io containers use s6-overlay, with
--env PUID=1000
and--env PGID=1000
it will run as UID=1000 inside the container. The three--uidmap
flags will map the container UID=1000 to intermediate UID=0. For rootless podman, the intermediate UID=0 with be mapped to the UID for the user starting Podman. So the data in mapped volumes will be owned byswag
user. See the manual for more detailed explanation of UID mapping. allow_host_loopback=true
will allow access the host loopback IP, default is 10.0.2.2, see podman-run(1). Since Nextcloud runs as a different user, you cannot create a bridged network. If the nextcloud container listen 4443 port on the host, the swag container could connect to the nextcloud container through host loopback address 10.0.2.2:4443. You do not need to create a firewall rule for 4443 port.DOCKER_MODS=
adddashboard
andauto-reload
mods. Check here for other available mods.
Check the log there will be an error message because we did not provide Cloudflare API token.
$ podman logs swag
... ERROR: Cert does not exist! Please see the validation error above. Make sure you entered correct credentials into the /config/dns-conf/cloudflare.ini file.
DNS records
Create A
record for www
, nextcloud
and dashboard
for mydomain.com point to your server ip address.
If using Cloudflare make sure it is DNS only not proxied/cached, the cloud logo should be grey not orange.
Create a Cloudflare API token https://dash.cloudflare.com/profile/api-tokens for mydomain.com with permission to edit DNS.
Edit
/home/swag/config/dns-conf/cloudflare.ini
# Replace with your values # With global api key: #dns_cloudflare_email = cloudflare@example.com #dns_cloudflare_api_key = 0123456789abcdef0123456789abcdef01234567 # With token (comment out both lines above and uncomment below): dns_cloudflare_api_token = 0123456789abcdef0123456789abcdef01234567
comment out dns_cloudflare_email
and dns_cloudflare_api_key
, then replace dns_cloudflare_api_token
with your token.
Change permission of this file
$ chmod 700 /home/swag/config/dns-conf/cloudflare.ini
Then restart the container
$ podman restart swag
Wait a while and check status it should say Successfully received certificate.
$ podman logs swag
... Successfully received certificate. ... Server ready
It is possible that the DNS record has not propagate yet, you can edit /etc/hosts
file on your machine.
/etc/hosts
192.168.122.2 www.mydomain.com 192.168.122.2 nextcloud.mydomain.com
If the server ip is 192.168.122.2
.
Now connect to https://www.mydomain.com should find the SWAG welcome page.
Generate swag.service file
Podman support auto-update via systemd. Generate a systemd/user service file for swag:
$ mkdir -p /home/swag/.config/systemd/user/ $ cd /home/swag/.config/systemd/user/ $ podman generate systemd \ --new \ --name \ --no-header \ --restart-policy=on-failure \ --container-prefix='' \ --files \ swag
This will create swag.service
in the current working directory.
Then enable swag.service
and podman-auto-update.timer
$ systemctl --user enable swag.service $ systemctl --user enable podman-auto-update.timer
This should start swag container at boot and automatically update the container. Reboot the server and check the SWAG welcome page https://www.mydomain.com.
Set up reverse proxy for Nextcloud
Copy example config
$ cd /home/swag/config/nginx/proxy-confs/ $ cp nextcloud.subdomain.conf.sample nextcloud.subdomain.conf
Edit
/home/swag/config/nginx/proxy-confs/nextcloud.subdomain.conf
... set $upstream_app 10.0.2.2; set $upstream_port 4443; ...
The upstream_app
is set to the host loopback IP.
Since the Nextcloud instance will running under different user, not in the same bridge network, so access it through host.
As an example, the Nextcloud service will listen on 4443 port.
nextcloud.subdomain.conf
, or see this
Nextcloud
Get an interactive shell as nextcloud
user
# machinectl shell nextcloud@
Create data directories
$ mkdir /home/nextcloud/data $ mkdir /home/nextcloud/config $ mkdir /home/nextcloud/database
Create nextcloud pod
$ podman pod create \ --replace \ --uidmap 1000:0:1 \ --uidmap 0:1:1000 \ --uidmap 1001:1001:64536 \ --publish 127.0.0.1:4443:443 \ --dns=1.1.1.1 \ --name nextcloud-pod
Here the 4443 port match the port set in #Set up reverse proxy for Nextcloud.
With 127.0.0.1
it only listen local connection.
Attach PostgreSQL container to the pod
$ podman run \ --pod=nextcloud-pod \ --rm \ --detach \ --replace \ --label io.containers.autoupdate=registry \ --name=postgres \ --user 1000:1000 \ --volume /home/nextcloud/database:/var/lib/postgresql/data:Z \ --env POSTGRES_DB=nextcloud \ --env POSTGRES_USER=nextcloud \ --env POSTGRES_PASSWORD=nextcloud_database_password \ docker.io/library/postgres:15-alpine
Attach nextcloud container to the pod
$ podman run \ --pod=nextcloud-pod \ --rm \ --detach \ --replace \ --label io.containers.autoupdate=registry \ --name=nextcloud \ --env PUID=1000 \ --env PGID=1000 \ --env TZ=US/Eastern \ --volume /home/nextcloud/config:/config:Z \ --volume /home/nextcloud/data:/data:Z \ lscr.io/linuxserver/nextcloud:latest
Now you can access Nextcloud with https://nextcloud.mydomain.com. It will show you the Nextcloud setup page. Configure the database: choose PostgreSQL, with database user/name/password specified in #Attach PostgreSQL container to the pod. The database host would be localhost.
Generate nextcloud systemd serivce files
To generate a systemd/user service file for Nextcloud:
$ mkdir -p /home/nextcloud/.config/systemd/user/ $ cd /home/nextcloud/.config/systemd/user/ $ podman generate systemd \ --new \ --name \ --no-header \ --restart-policy=on-failure \ --container-prefix='' \ --pod-prefix='' \ --files \ nextcloud-pod
Then enable nextcloud-pod.service
, nextcloud.service
, postgres.service
and podman-auto-update.timer
$ systemctl --user enable nextcloud-pod.service $ systemctl --user enable nextcloud.service $ systemctl --user enable postgres.service $ systemctl --user enable podman-auto-update.timer
Reboot the server and check if Nextcloud auto started.
Auto clean up
To automatically remove unused pods, containers, images, etc.
Create podman-system-prune.service
and podman-system-prune.timer
in systemd user configs directory, /home/swag/.config/systemd/user/
and /home/nextcloud/.config/systemd/user/
, with content
/home/username/.config/systemd/user/podman-system-prune.service
[Unit] Description=Remove all unused pods, containers, images, networks, and volume data [Service] ExecStart=/usr/bin/podman system prune --all --force --filter "until=240h"
This will remove containers and images created 10 days (240h) ago.
/home/username/.config/systemd/user/podman-system-prune.timer
[Unit] Description=podman image prune timer [Timer] OnCalendar=daily [Install] WantedBy=timers.target
Then enable the podman-system-prune.timer
for both swag
and nextcloud
user, will run the cleanup everyday.