Why Rootless Containers Matter for a Home‑NAS
When a home‑NAS sits on a single‑user machine, the first instinct is to give the NAS service full root access. That works, but it also opens a door for accidental or malicious privilege escalation. Rootless Podman lets you run containerized services—Nextcloud, Syncthing, or a simple Nginx reverse proxy—inside a user namespace that isolates the container from the host. The container can still read and write to your data directories, but it can’t touch the kernel or system services.
Rootless mode also removes the need for sudo or setuid binaries, shrinking the attack surface. It’s a solid fit for a self‑hosted NAS that’s exposed to the internet but should stay confined to the user’s home directory.
Prerequisites
| Item | Version | Notes |
|---|---|---|
| Fedora 39 (or any recent distro) | 39+ | Rootless Podman is baked in. |
podman | 4.8+ | sudo dnf install podman |
systemd | 252+ | For user‑service units. |
firewalld | 0.9+ | Optional, but recommended. |
socat | 1.7+ | For port forwarding in some setups. |
Tip: If you’re on Debian/Ubuntu, install
podmanfrom the official backports or useapt install podman. On Arch,pacman -S podman.
1. Preparing the Host
1.1 Create a Dedicated User
sudo useradd -m -s /usr/bin/bash nas
sudo passwd nas
Give the user a strong password and set up SSH keys if you plan to manage the NAS remotely.
1.2 Enable User‑Namespace Support
Most modern kernels already enable user namespaces. Verify:
cat /proc/sys/kernel/unprivileged_userns_clone
If the output is 0, enable it:
sudo sysctl -w kernel.unprivileged_userns_clone=1
Add to /etc/sysctl.d/99-userns.conf for persistence.
1.3 Install and Configure Firewalld
sudo dnf install firewalld
sudo systemctl enable --now firewalld
Open the ports you’ll expose (e.g., 80, 443, 8443 for HTTPS):
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
2. Setting Up the NAS Directory Structure
Create a data directory that the container will mount:
sudo mkdir -p /srv/nas
sudo chown nas:nas /srv/nas
Inside /srv/nas, create subdirectories for each service:
mkdir -p /srv/nas/nextcloud/data
mkdir -p /srv/nas/syncthing/config
mkdir -p /srv/nas/nginx/conf
Set appropriate permissions:
chmod 750 /srv/nas/nextcloud/data
chmod 750 /srv/nas/syncthing/config
chmod 750 /srv/nas/nginx/conf
3. Running a Simple Nginx Reverse Proxy
3.1 Pull the Image
podman pull docker.io/library/nginx:latest
3.2 Create a User Service File
Create /home/nas/.config/systemd/user/nginx.service:
[Unit]
Description=Rootless Nginx
After=network.target
[Service]
ExecStart=/usr/bin/podman run --rm \
-p 80:80 \
-p 443:443 \
-v /srv/nas/nginx/conf:/etc/nginx/conf.d:ro \
-v /srv/nas/nginx/html:/usr/share/nginx/html:ro \
docker.io/library/nginx:latest
Restart=on-failure
RestartSec=10
[Install]
WantedBy=default.target
Security note: The --rm flag ensures the container is removed on exit, preventing stale containers from lingering.
3.3 Enable and Start the Service
loginctl enable-linger nas
sudo systemctl enable --user nginx.service
sudo systemctl start --user nginx.service
Check status:
systemctl --user status nginx.service
You should see the container running and listening on ports 80/443. Verify with curl http://localhost.
4. Deploying Nextcloud
4.1 Pull the Official Image
podman pull docker.io/library/nextcloud:latest
4.2 Create a User Service File
/home/nas/.config/systemd/user/nextcloud.service:
[Unit]
Description=Rootless Nextcloud
After=network.target
[Service]
ExecStart=/usr/bin/podman run --rm \
-p 8080:80 \
-v /srv/nas/nextcloud/data:/var/www/html \
-v /srv/nas/nextcloud/config:/var/www/html/config \
docker.io/library/nextcloud:latest
Restart=on-failure
RestartSec=10
[Install]
WantedBy=default.target
4.3 Start the Service
sudo systemctl enable --user nextcloud.service
sudo systemctl start --user nextcloud.service
Open http://<NAS_IP>:8080 and complete the web installer. Use the data directory you created earlier for uploads.
5. Adding Syncthing
5.1 Pull the Image
podman pull docker.io/linuxserver/syncthing:latest
5.2 Create a User Service File
/home/nas/.config/systemd/user/syncthing.service:
[Unit]
Description=Rootless Syncthing
After=network.target
[Service]
ExecStart=/usr/bin/podman run --rm \
-p 8384:8384 \
-v /srv/nas/syncthing/config:/config \
-v /srv/nas/syncthing/data:/data \
docker.io/linuxserver/syncthing:latest
Restart=on-failure
RestartSec=10
[Install]
WantedBy=default.target
5.3 Start the Service
sudo systemctl enable --user syncthing.service
sudo systemctl start --user syncthing.service
Access the web UI at http://<NAS_IP>:8384. The configuration lives in /srv/nas/syncthing/config, so you can back it up or version‑control it.
6. Managing Storage with BTRFS Subvolumes
If your NAS uses BTRFS, you can create subvolumes for each service to isolate snapshots and quotas.
sudo btrfs subvolume create /srv/nas/nextcloud
sudo btrfs subvolume create /srv/nas/syncthing
sudo btrfs subvolume create /srv/nas/nginx
Mount them with subvol= in /etc/fstab:
/dev/sda1 /srv/nas btrfs defaults,subvol=nas 0 0
This keeps the container data separate from the rest of the filesystem, making snapshots cleaner.
7. Security Hardening Tips
| Area | Recommendation |
|---|---|
| User Namespaces | Keep unprivileged_userns_clone enabled only for the NAS user. |
| File Permissions | Use 750 on data directories; only the NAS user and root can read/write. |
| Firewalls | Limit inbound traffic to only required ports. Use firewall-cmd --zone=public --add-port=8080/tcp for Nextcloud. |
| TLS | Run a separate container (e.g., Caddy) as a reverse proxy with Let’s Encrypt certificates. |
| Updates | Automate podman pull and systemctl restart via cron or systemd timers. |
| Audit | Enable auditd and monitor /var/log/audit/audit.log for unexpected execve calls. |
8. Troubleshooting Common Issues
8.1 “Failed to create user namespace” Error
podman run --userns=keep-id ...
If the kernel disallows user namespaces for the user, double‑check /proc/sys/kernel/unprivileged_userns_clone. Also ensure the user’s UID is not in the range reserved for system users (0–999).
8.2 Port Conflicts
If another service occupies port 80, either change the container’s exposed port or stop the conflicting service:
sudo systemctl stop httpd
8.3 Data Loss After Container Restart
Rootless containers use the user’s home directory for storage. If the container is removed (--rm), volumes persist. However, if you mount a host directory that is not owned by the NAS user, the container may not write to it. Verify ownership:
ls -ld /srv/nas/nextcloud/data
8.4 SELinux Denials
On Fedora, SELinux may block container writes. Use chcon -R -t svirt_sandbox_file_t /srv/nas/* or set --security-opt label=disable in the ExecStart line. Prefer the former for a more secure setup.
9. Automating Updates
Create a systemd timer to pull the latest images nightly and restart services.
/home/nas/.config/systemd/user/refresh-nas.timer:
[Unit]
Description=Refresh NAS containers
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
[Install]
WantedBy=timers.target
/home/nas/.config/systemd/user/refresh-nas.service:
[Unit]
Description=Pull latest images and restart services
[Service]
Type=oneshot
ExecStart=/usr/bin/bash -c '
podman pull docker.io/library/nginx:latest
podman pull docker.io/library/nextcloud:latest
podman pull docker.io/linuxserver/syncthing:latest
systemctl --user restart nginx.service
systemctl --user restart nextcloud.service
systemctl --user restart syncthing.service
'
Enable the timer:
sudo systemctl enable --user refresh-nas.timer
sudo systemctl start --user refresh-nas.timer
10. Scaling Beyond a Single Host
If you later add a second NAS or move to a cloud VM, you can use podman-compose or podman generate systemd to export the current configuration into a reproducible format. The rootless approach remains unchanged; only the host user and storage paths need adjustment.
11. Final Thoughts
Rootless Podman gives you a lightweight, secure way to run containerized services on a home‑NAS. By keeping the containers in a user namespace, you avoid the pitfalls of running as root while still enjoying the flexibility of containers. The workflow above is straightforward, relies on standard systemd user units, and can be extended with TLS, backups, or more services without changing the core security posture.
Happy self‑hosting!
See also
- Why my SSH key‑based login keeps timing out after a system reboot – and how to fix it with ssh‑config and ssh‑agent
- Turn /tmp into a tmpfs so you never run out of space during a log surge
- Avoiding the “Broken Packages” Hang When Upgrading From Ubuntu 22.04 to 24.04 on a Home Server
- When systemd Unit Files Don’t Pick Up Changes: Using systemctl daemon-reload and reload-or-restart Wisely
- Resolving “Permission denied” When Moving Files Into a Sticky‑Bit Directory Using ACLs