- Showcase video: https://www.youtube.com/watch?v=FrCaie-2YKk
Introductions
I wanted to set up a simple self-hosted photo backup server with Immich on RHEL.
Most Immich guides are written for setups that do not have to deal with RHEL-specific SELinux behavior. The installation itself is not too bad, but getting the permissions right can be frustrating if you have never done it before.
This post walks through how to run Immich on RHEL with rootless Podman and Tailscale, with the extra steps needed to make it work cleanly.
Step 1: Download the Immich Files (default setup)
First, create the working directory and pull down the official Immich deployment files.
| |
Step 2: Install podman-compose
Now we need a way to run this compose file. RHEL keeps its official repositories incredbly strict for enterprise stability. To get community-standard tools like podman-compose, we have to switch to root, enable CodeReady Builder (CRB), and get into Fedora’s Extra Packages for Enterprise Linux (EPEL).
| |
Tip: If the CRB repository does not work, you can always bypass it by installing pip and installing podman-compose through Python instead.
| |
Step 3: Edit SELinux Contexts
Next, edit the docker-compose.yml file. Because RHEL uses SELinux, it blocks containers from touching host files. We have to explicitly add permissions to the volume mounts inside the file.
| |
| |
When you are defining your volume paths in the compose file, append the correct SELinux labels:
- Add a lowercase
:zto your photo uploads and database volume folder so multiple Immich containers can share that directory without locking each other out.
Step 4: Fix PostgreSQL Permissions for Rootless Podman
Before starting the server, there is one major catch with rootless Podman. Because we are running this securely as a normal user instead of root, the host machine doesn’t recognize the container’s internal PostgreSQL user. We have to translate those permissions.
| |
The podman unshare command drops us into the container’s user namespace. It maps the container’s internal database user to a large, unprivileged user ID on the host machine, fixing the permission-denied errors without compromising security.
Step 5: Fire It Up
With the permissions locked in and the files configured, start the stack in detached mode.
| |
Step 6: Secure Remote Access with Tailscale
The server is running, but how do you access it securely without exposing ports to the internet? Tailscale makes this effortless.
Install Tailscale using their official script:
| |
Once installed, bring the node online:
| |
Copy the authentication link provided in the terminal, paste it into your browser, and authenticate. That’s it. Anything connected to your Tailscale mesh network can now access your new Immich instance securely.
Convert Containers Into a Service with Quadlet
If you want to convert your containers into a proper user service, follow these steps. With the following steps, you can have immich auto start if the system restarts and so on.
Step 7: Transition to Quadlet (Kubernetes Style)
We can use Quadlet to treat the containers as a native systemd service. First, generate a Kubernetes YAML blueprint from the stack.
| |
Step 8: Networking in a Pod
Inside a Pod, containers share the same network stack. That means Immich needs to resolve database and redis to 127.0.0.1. Add this to immich.yaml.
| |
Step 9: Configure Quadlet
Move your manifest to the systemd generator directory and create the .kube file.
| |
immich.kube content:
| |
Step 10: Enable and Boot
Enable lingering so the service keeps running even when you are not logged in, then start it.
| |
Verification
Check logs with:
| |
If you see database system is ready, the service is up.