> For the complete documentation index, see [llms.txt](https://docs.linuxguard.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.linuxguard.io/install/install/container/podman.md).

# Podman

Podman is a daemonless container engine that runs containers as ordinary user processes. The LinuxGuard agent runs under Podman with two distinct trade-offs: rootful (full eBPF telemetry, ergonomically equivalent to Docker) or rootless (least-privilege execution, restricted telemetry surface). This page documents both shapes plus systemd quadlet integration for hosts where the agent should be a system service.

> **Important**: **Rootless Podman cannot grant `CAP_BPF` + `CAP_PERFMON` to the container** because user namespaces do not propagate the host's eBPF capabilities to the container's PID-1. Rootless agents run in `DEGRADED_PROBES_PARTIAL` mode — the agent process starts and produces non-eBPF telemetry, but no behavioral, auth, or file-monitor events are produced. Use rootful Podman when full telemetry coverage is required.

## When to use Podman

* **Hosts where Docker daemon access is restricted** (locked-down workstations, hardened servers, multi-tenant build hosts). Podman's daemonless model fits the security posture.
* **systemd-native deployments** where the agent should be a `--user` or system service managed by `systemctl`. The quadlet integration is canonical for this shape.
* **RHEL / Fedora / SUSE hosts** where Podman is the supported container engine and Docker is not packaged.

If your host runs Docker, the docker-compose shape ([docker-compose deployment](/install/install/container/docker-compose.md)) is simpler — Podman's `podman-compose` is API-compatible but has fewer eyes on it.

## Rootless vs rootful trade-offs

| Property                    | Rootful Podman                                | Rootless Podman                                                                              |
| --------------------------- | --------------------------------------------- | -------------------------------------------------------------------------------------------- |
| eBPF probes                 | Yes (full)                                    | **No** — user namespaces drop the agent's caps; loader degrades to `DEGRADED_PROBES_PARTIAL` |
| Behavioral telemetry        | Yes                                           | **No**                                                                                       |
| Auth events                 | Yes                                           | **No**                                                                                       |
| File monitor                | Yes                                           | **No**                                                                                       |
| Configuration management    | Yes                                           | Yes                                                                                          |
| Package inventory           | Yes                                           | Yes                                                                                          |
| Syslog forwarding           | Yes                                           | Yes                                                                                          |
| `support-bundle collect`    | Yes                                           | Yes                                                                                          |
| Privilege required to start | `sudo podman` or member of the `podman` group | Any user; no privilege elevation                                                             |
| Host filesystem visibility  | Full                                          | User-namespace remapped                                                                      |
| `pid=host`                  | Available                                     | Available but limited to the user's own processes                                            |
| Image storage location      | `/var/lib/containers/`                        | `~/.local/share/containers/`                                                                 |

**Choose rootful when** behavioral telemetry coverage is required (the standard case for a security agent).

**Choose rootless when** the host's security policy forbids daemonless privileged containers AND the agent's role is limited to configuration management, package inventory, or syslog forwarding (a narrow surface).

## Rootful Podman

The rootful shape is ergonomically equivalent to a Docker run with the same flags. The only difference is the engine binary.

### Minimal invocation

```bash
sudo podman run -d \
  --name linuxguard-agent \
  --restart unless-stopped \
  --pid=host \
  --cap-add=BPF --cap-add=PERFMON --cap-add=DAC_READ_SEARCH \
  --cap-add=SYS_PTRACE --cap-add=SETPCAP \
  --security-opt seccomp=unconfined \
  --tmpfs /run/linuxguard:rw,mode=0700,size=10m \
  -v /sys/kernel/tracing:/sys/kernel/tracing:ro \
  -v /sys/fs/bpf:/sys/fs/bpf \
  -v /proc:/host/proc:ro \
  --read-only \
  -e LINUXGUARD_ENROLL_TOKEN="$ENROLL_TOKEN" \
  -e LINUXGUARD_TENANT_ID="$TENANT_ID" \
  -e LINUXGUARD_NODE_NAME="$(hostname)" \
  -e LINUXGUARD_POD_UID="$(uuidgen)" \
  packages.linuxguard.io/linuxguard-agent:v3.0.0 \
  start --tls-cache
```

The flags match the Docker equivalent line-for-line; see [docker-compose deployment](/install/install/container/docker-compose.md) for the rationale on each.

### Differences from Docker

* **`sudo` prefix** — rootful Podman requires either `sudo` or membership in the `podman` group. The `podman` group has no default member; add users explicitly via `usermod -aG podman <user>`.
* **No daemon to start** — Podman is daemonless. There is no `systemctl start podman` for the rootful case (the `podman.socket` unit is used only for the Docker-compatibility API).
* **Image storage** — rootful Podman stores images in `/var/lib/containers/`. Free space requirements match Docker's `/var/lib/docker/`.
* **SELinux labeling** — on RHEL/CentOS/Fedora hosts with SELinux enforcing, Podman applies the `container_t` type to the container's processes. Host-path mounts may require `:z` or `:Z` suffix for SELinux relabeling — see [§ SELinux considerations](#selinux-considerations).

## Rootless Podman

Rootless Podman runs the container in a user namespace owned by the invoking user. Capabilities granted at `--cap-add` time are scoped to that namespace, not the host's. The agent's eBPF loader detects this at startup and degrades to `DEGRADED_PROBES_PARTIAL` mode.

### Minimal invocation (rootless)

```bash
podman run -d \
  --name linuxguard-agent \
  --restart unless-stopped \
  --tmpfs /run/linuxguard:rw,mode=0700,size=10m \
  --read-only \
  -e LINUXGUARD_ENROLL_TOKEN="$ENROLL_TOKEN" \
  -e LINUXGUARD_TENANT_ID="$TENANT_ID" \
  -e LINUXGUARD_NODE_NAME="$(hostname)" \
  -e LINUXGUARD_POD_UID="$(uuidgen)" \
  packages.linuxguard.io/linuxguard-agent:v3.0.0 \
  start --tls-cache
```

Notes on what is OMITTED from the rootless invocation:

* No `--cap-add` flags — the capabilities are scoped to the user namespace and have no effect on the host eBPF subsystem. The agent will log a startup warning about degraded mode.
* No `--pid=host` — rootless Podman cannot grant host PID visibility; the agent sees only the user's own processes.
* No tracefs bind-mount — without `CAP_BPF` the agent does not attempt probe attach.
* No host `/proc` mount.

### Verifying degraded mode

After the agent starts, the log should contain a clear degradation marker:

```
{"level":"warn","msg":"eBPF probes unavailable in this context — DEGRADED_PROBES_PARTIAL mode"}
```

If your security policy permits, switch to the rootful shape to recover the eBPF telemetry surface. Otherwise, document the rootless deployment as a known coverage gap.

## systemd quadlet integration

A quadlet is a systemd unit file that describes a Podman container in a declarative, systemd-native way. Quadlets are preferred over `podman generate systemd` (which generates verbose units that mix Podman state into systemd state) because they are simpler, idempotent, and version-controlled like any other systemd unit.

### Rootful quadlet

Place the following file at `/etc/containers/systemd/linuxguard-agent.container`:

```ini
[Unit]
Description=LinuxGuard agent (rootful)
After=network-online.target
Wants=network-online.target

[Container]
Image=packages.linuxguard.io/linuxguard-agent:v3.0.0
ContainerName=linuxguard-agent
# PID 1 auto-detection inside the container.
PidsLimit=2048
PodmanArgs=--pid=host
AddCapability=BPF
AddCapability=PERFMON
AddCapability=DAC_READ_SEARCH
AddCapability=SYS_PTRACE
AddCapability=SETPCAP
DropCapability=ALL
SecurityLabelType=container_t
# UAT/dev — replace with a custom profile for production.
PodmanArgs=--security-opt=seccomp=unconfined
ReadOnly=true
Tmpfs=/run/linuxguard:rw,mode=0700,size=10m
Volume=/sys/kernel/tracing:/sys/kernel/tracing:ro
Volume=/sys/fs/bpf:/sys/fs/bpf
Volume=/proc:/host/proc:ro
# Source the token from /etc/linuxguard/enroll.env — restricted to root + linuxguard group.
EnvironmentFile=/etc/linuxguard/enroll.env
Exec=start --tls-cache

[Service]
Restart=always
RestartSec=10
TimeoutStartSec=300

[Install]
WantedBy=default.target
```

Generate the systemd unit and enable it:

```bash
# systemd reads quadlet files on each reload; no separate `generate` step needed.
sudo systemctl daemon-reload
sudo systemctl enable --now linuxguard-agent.service
sudo systemctl status linuxguard-agent.service
```

The `EnvironmentFile=/etc/linuxguard/enroll.env` references a file with the token + tenant ID:

```
# /etc/linuxguard/enroll.env — chmod 0640, owned by root:linuxguard
LINUXGUARD_ENROLL_TOKEN=<TOTP_TOKEN>
LINUXGUARD_TENANT_ID=<TENANT_ID>
LINUXGUARD_NODE_NAME=<HOSTNAME>
LINUXGUARD_POD_UID=<UUID>
```

Restrict permissions on the env file:

```bash
sudo install -d -m 0750 -o root -g linuxguard /etc/linuxguard
sudo install -m 0640 -o root -g linuxguard /dev/null /etc/linuxguard/enroll.env
sudo $EDITOR /etc/linuxguard/enroll.env   # populate with the token + tenant
```

Only root and the `linuxguard` group can read the env file. systemd reads it as root when the service starts; the value is then injected into the container's environ block via Podman.

### Rootless quadlet

Place the file at `~/.config/containers/systemd/linuxguard-agent.container` (note the user-scoped path):

```ini
[Unit]
Description=LinuxGuard agent (rootless)
After=default.target

[Container]
Image=packages.linuxguard.io/linuxguard-agent:v3.0.0
ContainerName=linuxguard-agent
ReadOnly=true
Tmpfs=/run/linuxguard:rw,mode=0700,size=10m
EnvironmentFile=%h/.config/linuxguard/enroll.env
Exec=start --tls-cache

[Service]
Restart=always
RestartSec=10

[Install]
WantedBy=default.target
```

Enable as a user service:

```bash
# Reload the user-systemd generator so the quadlet is picked up.
systemctl --user daemon-reload
systemctl --user enable --now linuxguard-agent.service
systemctl --user status linuxguard-agent.service

# Enable lingering so the user service persists across logout.
loginctl enable-linger "$USER"
```

The rootless quadlet inherits the same degradation properties as the rootless `podman run` invocation — see [§ Rootless Podman](#rootless-podman).

## Docker-equivalent commands

For operators familiar with Docker, the table below maps common workflows to their Podman equivalents. The agent's behavior is identical under either engine.

| Docker                               | Podman                               | Notes                                                                                                  |
| ------------------------------------ | ------------------------------------ | ------------------------------------------------------------------------------------------------------ |
| `docker run ...`                     | `podman run ...`                     | Identical syntax. Rootful Podman requires `sudo`.                                                      |
| `docker compose up -d`               | `podman-compose up -d`               | `podman-compose` is a Python wrapper; install with `pip install podman-compose` or via distro package. |
| `docker ps`                          | `podman ps`                          | Identical.                                                                                             |
| `docker logs -f linuxguard-agent`    | `podman logs -f linuxguard-agent`    | Identical.                                                                                             |
| `docker exec linuxguard-agent <cmd>` | `podman exec linuxguard-agent <cmd>` | Identical. The distroless image has no shell; pass a specific command.                                 |
| `docker stop linuxguard-agent`       | `podman stop linuxguard-agent`       | Identical. Sends SIGTERM; agent exits 143.                                                             |
| `docker pull <image>`                | `podman pull <image>`                | Identical.                                                                                             |
| `docker login <registry>`            | `podman login <registry>`            | Identical. Credentials stored in `~/.config/containers/auth.json`.                                     |
| `docker inspect <ctr>`               | `podman inspect <ctr>`               | Identical output structure.                                                                            |
| `docker system df`                   | `podman system df`                   | Identical.                                                                                             |

## SELinux considerations

On RHEL / CentOS / Fedora / Rocky Linux hosts with SELinux enforcing, Podman applies the `container_t` type to processes inside the container. Host-path mounts must be relabeled or the agent's reads from `/sys/kernel/tracing` are denied with `avc: denied`.

Two options:

### Option 1: `:z` / `:Z` mount suffix

Append `:z` (shared label) or `:Z` (private label) to the `-v` flag value. This is the simplest approach for single-host deployments:

```bash
-v /sys/kernel/tracing:/sys/kernel/tracing:ro,z
```

`:z` relabels the host directory so the container can read it AND other containers can read it (shared). `:Z` relabels for exclusive access by this one container. For `/sys/kernel/tracing` (a kernel pseudo-filesystem, not real disk), `:z` is appropriate.

### Option 2: Set the agent's SELinux profile at host level

Install the SELinux policy that ships with the agent's packaging tree, then label the agent's process with the policy's domain:

```bash
sudo semodule -i /usr/share/linuxguard/selinux/linuxguard-agent.pp
sudo semanage fcontext -a -t linuxguard_agent_exec_t '/usr/local/bin/linuxguard-agent'
sudo restorecon -v /usr/local/bin/linuxguard-agent
```

This is the production path. It avoids the `:z` / `:Z` relabel side effect on host directories.

### Verifying SELinux is not blocking

```bash
sudo ausearch -m AVC -ts recent | grep linuxguard
```

If recent AVC denials appear, set SELinux to permissive temporarily to confirm the agent works in the absence of policy enforcement:

```bash
sudo setenforce 0
podman logs linuxguard-agent | grep heartbeat
sudo setenforce 1
```

Then apply Option 1 or Option 2 to allow the agent under enforcing mode.

## Security context

The rootful Podman security context is equivalent to the Kubernetes DaemonSet shape — see [Kubernetes DaemonSet § Line-by-line rationale](/install/install/container/kubernetes-daemonset.md#line-by-line-rationale) for the per-capability rationale. The rootless Podman security context inherits the user namespace's degradation as documented in [§ Rootless vs rootful trade-offs](#rootless-vs-rootful-trade-offs).

Common to both: `read_only` filesystem, tmpfs for the TLS cache, no privilege escalation needed (file caps baked into the binary), `nonroot` user (UID 65532 inside the container, remapped to a high UID range in the rootless case).

## Host paths

Identical to the Kubernetes DaemonSet host-path requirements for the rootful case — see [Kubernetes DaemonSet § Host paths](/install/install/container/kubernetes-daemonset.md#host-paths). The rootless case omits the host-path mounts entirely because the user namespace cannot productively use them.

> **Security Note**: For SELinux-enforcing hosts, mount each host path with `:z` (or apply the SELinux module per Option 2 above). For the rootless case, do not mount host paths — the user namespace cannot use them.

## Pod Security Standard compatibility

PSS is a Kubernetes concept; it does not apply to Podman. The Podman equivalent is the combination of `--cap-add` / `--cap-drop`, `--security-opt`, `--read-only`, `--tmpfs`, `--pid`, and the rootful-vs-rootless distinction. The rootful Podman shape corresponds to PSS `privileged` (because of `--pid=host` and the added capabilities); the rootless shape has no Kubernetes analogue.

## RBAC

RBAC is a Kubernetes concept; it does not apply to Podman. The Podman equivalent is filesystem access control on the Podman binary and the user namespace:

* **Rootful** — access is controlled by `sudo` policy or `podman` group membership. Restrict membership to trusted users.
* **Rootless** — access is per-user. The Podman binary itself is unprivileged; the user namespace determines what the container can see.

For the quadlet integration, systemd's unit-file permissions (`/etc/containers/systemd/` for rootful, `~/.config/containers/systemd/` for rootless) control who can modify the agent's deployment manifest.

## Verification

After starting the agent (via `podman run`, `podman-compose`, or the quadlet service):

```bash
# Confirm the container is running.
podman ps --filter name=linuxguard-agent

# Watch the log for the heartbeat line (rootful — expect heartbeat).
podman logs --tail=50 linuxguard-agent | grep -E 'heartbeat|degraded|error'

# Rootless — expect the DEGRADED_PROBES_PARTIAL warning.
# This is normal for rootless; do NOT treat it as an error.

# Verify probe status.
podman exec linuxguard-agent /usr/local/bin/linuxguard-agent probe --pretty
```

A healthy rootful agent produces a `heartbeat` log line within 20 seconds. A degraded rootless agent produces the degradation warning, then a heartbeat with the reduced telemetry surface.

***

**Next Step**: [OCI multi-arch manifest →](/install/install/oci-multi-arch-manifest.md)

**Related**: [Container deployment hub](/install/install/container.md) | [docker-compose deployment](/install/install/container/docker-compose.md) | [Kubernetes DaemonSet](/install/install/container/kubernetes-daemonset.md) | [Distroless image reference](/install/install/container/distroless.md) | [`probe` CLI reference](/reference/reference/cli/probe.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.linuxguard.io/install/install/container/podman.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
