Podman : Rootless Containers | Chat 2 | Chat 3 | Chat 4

In rootless Podman, the container’s root user is actually a non-root user on the host, mapped via user namespaces.

Namespace and UID

Mappings in Rootless Podman:

Create subuid/subgid range per user

sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 auser

Dockerfile mods

RUN useradd -m auser
USER auser
RUN chown -R auser /path/to/app

Host : User Namespaces

In Podman rootless mode, a container runs in a separate user namespace, which isolates the user and group IDs inside the container from the ones on the host. Even though a process may run as UID 0 (root) inside the container, it is actually mapped to a non-root user ID on the host.

Customizing UID/GID Mappings

You can modify the default UID/GID mappings by editing /etc/subuid and /etc/subgid. You can also specify custom mappings when running a container using the --uidmap and --gidmap options in podman run.


podman run --uidmap 0:100000:1000 --gidmap 0:100000:1000 my-image

Issues

Persist a Rootless Container

Q:

Rootless podman containers stop when user logs out?

A:

Yes, rootless Podman containers will stop when the user logs out by default. This is because they are tied to the user's session and their processes terminate when the session ends.

To prevent rootless Podman containers from stopping upon logout, you can use the Podman service or systemd integration:

app=bbox

# Configure to always restart
podman run -d --restart=always --name $app busybox sleep 1d

# Generate user-scoped systemd service file : DEPRICATED : use Quadlets
podman generate systemd --new --name $app --files

# Move the systemd service file:
mkdir -p ~/.config/systemd/user 
mv container-$app.service ~/.config/systemd/user/

# Enable/Start the service on user login
systemctl --user enable --now container-$app.service

# Enable lingering
loginctl enable-linger $(whoami)

# Teardown
podman container stop $app
podman container rm $app 

This approach ensures that your rootless Podman containers remain running even when you log out.

Quadlets

Quadlets are essentially a systemd unit generator specifically for containers. Instead of manually writing complex .service files, you can use .container files (a simplified format) to define container behaviors, such as the image to use, environment variables, volumes, and restart policies.

These .container files are easier to write and maintain compared to traditional .service files that might call podman run commands. Systemd then reads these Quadlet .container files and generates the appropriate .service units behind the scenes.

Setup:

app=bbox

# Create the systemd .container file
mkdir -p ~/.config/systemd/user 
cat <<-EOH |tee ~/.config/systemd/user/$app.container
[Container]
Image=docker.io/library/busybox:latest
Name=$app
Command=/usr/bin/start-app --flag
Volume=/host/data:/data:rw
Volume=/host/config:/config:ro
Network=host
Port=8080:80
Environment=ENV_VAR=value
Environment=TLS_CERT=/config/cert.pem
Environment=TLS_KEY=/config/key.pem
WorkDir=/app
Restart=always
EOH

# Enable/Start the service on user login
systemctl --user enable --now $app.container

# Enable lingering
loginctl enable-linger $(whoami)

This approach is cleaner and recommended if you are seeing warnings about Quadlets. Quadlets simplify the management of containers using systemd while retaining the flexibility of systemd service management.

podman play kube

Define the pod and its containers declaratively using Podman's podman play kube feature. This allows you to describe the entire pod (including its containers, volumes, and networking) in a K8s-compatible YAML file. The systemd service can then use this YAML file to start and manage the pod without requiring out-of-band podman commands.

Here’s how to do it:


1. Create a K8s-Compatible YAML File

Define the pod and its containers in a YAML file. Save it as /path/to/registry-pod.yaml:

apiVersion: v1
kind: Pod
metadata:
  name: registry-pod
spec:
  containers:
    - name: registry
      image: docker.io/library/registry:2
      volumeMounts:
        - name: registry-data
          mountPath: /var/lib/registry
      ports:
        - containerPort: 5000

    - name: nginx
      image: docker.io/library/nginx:alpine
      volumeMounts:
        - name: nginx-config
          mountPath: /etc/nginx/conf.d
        - name: nginx-ssl
          mountPath: /etc/nginx/ssl
      ports:
        - containerPort: 443

  volumes:
    - name: registry-data
      hostPath:
        path: /path/to/registry/data
        type: Directory
    - name: nginx-config
      hostPath:
        path: /path/to/nginx/conf.d
        type: Directory
    - name: nginx-ssl
      hostPath:
        path: /path/to/nginx/ssl
        type: Directory

2. Create the NGINX Configuration

Create the NGINX configuration file for the registry at /path/to/nginx/conf.d/registry.conf:

server {
    listen 443 ssl;
    server_name registry.example.com;

    ssl_certificate /etc/nginx/ssl/registry.crt;
    ssl_certificate_key /etc/nginx/ssl/registry.key;

    client_max_body_size 0; # Disable body size check for large uploads

    location / {
        proxy_pass http://localhost:5000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

3. Create a Systemd Service

Create a systemd service file to manage the pod using the YAML file. Save it as /etc/systemd/system/registry-pod.service:

[Unit]
Description=CNCF Distribution Registry Pod (Podman)
After=network.target

[Service]
ExecStart=/usr/bin/podman play kube /path/to/registry-pod.yaml
ExecStop=/usr/bin/podman pod stop -t 10 registry-pod
ExecStopPost=/usr/bin/podman pod rm -f registry-pod
Restart=always

[Install]
WantedBy=multi-user.target

Reload systemd and start the service:

sudo systemctl daemon-reload
sudo systemctl start registry-pod
sudo systemctl enable registry-pod

4. Verify the Setup


5. Optional: Secure the Registry


Summary

This approach uses a declarative YAML file to define the pod and its containers, which is then managed by a systemd service. The podman play kube command reads the YAML file and creates the pod, ensuring that the entire setup is self-contained and reproducible without requiring out-of-band podman commands.