> For the complete documentation index, see [llms.txt](https://docs.roboflow.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.roboflow.com/deployment/self-hosted/enterprise/secure-gateway-podman.md).

# Secure Gateway on RHEL with Podman

Deploy Secure Gateway on Red Hat Enterprise Linux hosts without Kubernetes, with the gateway and its encrypted cache running as Podman containers managed by Roboflow Deployment Manager.

[Secure Gateway](/deployment/self-hosted/enterprise/secure-gateway.md) also runs on Red Hat Enterprise Linux hosts that have neither Docker nor Kubernetes. A dedicated installer deploys the gateway and its encrypted cache as Podman containers, supervised by the Roboflow Deployment Manager (`rfdm`), which drives Podman through its Docker-compatible API. You get the same gateway as the Docker deployment (one controlled point of egress that proxies the Roboflow API and caches model weights and container images for your fleet) with the same air-gapped bundle model, on the container runtime that ships with RHEL.

{% hint style="info" %}
Three deployment models are available. Use the [Docker deployment](/deployment/self-hosted/enterprise/secure-gateway.md) on hosts that already run Docker, this page on RHEL hosts without Docker or Kubernetes, and [Secure Gateway on MicroShift](/deployment/self-hosted/enterprise/secure-gateway-microshift.md) on Red Hat Device Edge hosts managed as a Kubernetes fleet. The gateway's configuration reference (caching, TLS variables, log export) is shared across all three: see [Secure Gateway](/deployment/self-hosted/enterprise/secure-gateway.md) and the [Secure Gateway manual](https://secure-gateway.roboflow.com/manual/).
{% endhint %}

## Architecture

* The Roboflow Deployment Manager runs on the host as a systemd service and manages the containers through Podman's Docker-compatible API (`podman.socket`). It keeps the gateway configured, updated, and running.
* The gateway and its cache backend (SeaweedFS, an S3-compatible store with encryption at rest) run as containers under the system (root) Podman instance.
* The gateway publishes ports `80` and `443` directly on the host: HTTPS on `443`, with HTTP on `80` redirecting to HTTPS.
* The cache is stored in `/data/seaweedfs` on the host filesystem, bind-mounted into the cache container. No LVM volume group or storage provisioner is required.
* SELinux stays enforcing. Volume mounts in the managed container configuration carry SELinux labels (`:Z`/`z`), so no manual relabeling and no permissive mode are needed.

This is a single-host deployment. High availability and multi-host clusters are not supported.

{% hint style="warning" %}
Use a dedicated host for the gateway. Podman and CRI-O (the runtime MicroShift uses) share the host's container storage, so image cleanup in one (ex: `podman image prune`) can reclaim images the other runtime cached. If the host runs MicroShift, use the [MicroShift deployment](/deployment/self-hosted/enterprise/secure-gateway-microshift.md) instead.
{% endhint %}

## Prerequisites

* A Red Hat Enterprise Linux 9.x host on x86\_64, with Podman 4.x or newer. The installer bundle is built for `linux-amd64` only. Podman comes from the RHEL repositories with no extra subscription, but a minimal installation may not have it: run `sudo dnf install podman` first if `podman --version` fails. See the [support matrix](#support-matrix).
* Root access: the gateway installs under the system (root) Podman instance
* Disk: about 3 GB on `/` for the container images, plus room for the cache under `/data`, which grows to 50 GB
* Ports `80` and `443` available for the gateway on the host
* For connected installs, outbound HTTPS to `api.roboflow.com` and `repo.roboflow.com`

{% hint style="info" %}
Unlike the MicroShift deployment, there is no Kubernetes distribution to install: no OpenShift pull secret and no LVM volume group. Your Red Hat subscription only needs to cover RHEL itself.
{% endhint %}

## Install on a connected host

Download the Podman installer bundle and verify its integrity:

```bash
curl -fOL https://repo.roboflow.com/rfdm/secure-gateway/podman/latest/secure-gateway-podman-installer-linux-amd64.tar.gz
curl -fOL https://repo.roboflow.com/rfdm/secure-gateway/podman/latest/secure-gateway-podman-installer-linux-amd64.tar.gz.sha256

echo "$(cat secure-gateway-podman-installer-linux-amd64.tar.gz.sha256)  secure-gateway-podman-installer-linux-amd64.tar.gz" \
  | sha256sum -c -
```

Extract it and run the installer as root:

```bash
tar xzf secure-gateway-podman-installer-linux-amd64.tar.gz
cd secure-gateway-podman-installer

sudo ./install-podman.sh \
    --api-key   <ROBOFLOW_API_KEY> \
    --device-id <DEVICE_ID> \
    --workspace <WORKSPACE>
```

The installer:

1. Enables the Podman API socket (`podman.socket`), the Docker-compatible API the Deployment Manager manages containers through.
2. Loads the bundled Secure Gateway and SeaweedFS images into the host's root Podman storage. Nothing is pulled from a registry.
3. Switches Podman's network backend to netavark if legacy CNI leftovers on the host would otherwise break DNS between containers.
4. Generates the gateway's TLS certificate and private key.
5. Installs the Roboflow Deployment Manager as a systemd service in Podman mode and writes its device configuration.
6. Enables `podman-restart.service` so the containers come back after a host reboot.
7. Starts the Deployment Manager, which creates and starts the gateway and cache containers.

Verify the deployment:

```bash
sudo systemctl is-active rfdm
sudo podman ps
curl -fk https://<host-ip>/health
```

The gateway and cache containers should both be `Up`, and the health check should return HTTP 200. The `-k` flag is needed until you trust the gateway's certificate; see [TLS and trust](#tls-and-trust).

## Install air-gapped

The same bundle installs on hosts with no internet access. The container images ship inside it as OCI archives and are loaded directly into the host's Podman storage. The containers reference exact version tags, so nothing is ever pulled from a registry, at install time or later.

Download and verify the bundle on a connected machine, transfer it to the host by removable media or your internal file transfer process, then extract and run `install-podman.sh` exactly as above. Verification is the same.

## TLS and trust

The installer generates a self-signed certificate for the gateway, which serves HTTPS with it on port `443` and redirects HTTP on port `80` to HTTPS. The generated certificate uses the common name `repo.roboflow.com`, also covers `*.roboflow.com`, `localhost`, and `127.0.0.1`, and is valid for 10 years. Re-running the installer keeps that generated pair while more than a year is left on it, so trust you already distributed to clients keeps working. This applies only to the generated material: the installer tracks it separately, so a re-run without `--cert` and `--key` overwrites a certificate you supplied with a self-signed one.

The gateway reads its certificate and key from `/etc/secure-gateway/tls/tls.crt` and `/etc/secure-gateway/tls/tls.key` on the host. Pass `--cert` and `--key` to `install-podman.sh` to install your own material from the start, or replace those two files and restart the gateway container. Your certificate has to cover `repo.roboflow.com`: the client install script repoints that hostname at the gateway in `/etc/hosts`, and container runtimes check the TLS hostname before pulling images.

The gateway process runs as UID 1000 with group 0 inside its container, so it reads the files through their owner permissions. The installer sets both files to owner `1000:0`, mode `0640` on the key and `0644` on the certificate, and the directory to owner `root:0` and mode `0750`. Replacing the files as root and leaving them root-owned is the most common cause of a gateway that can no longer read its key (see [Troubleshooting](#troubleshooting)).

## Connecting Inference Servers

On each machine that runs a Roboflow Inference Server, run the gateway's client install script. It adds the gateway's certificate to the machine's trust store and routes its Roboflow traffic (API calls, model weights, and container image pulls) through the gateway:

```bash
curl -fsSLk https://<host-ip>/install-client.sh | sudo bash -s -- --server <host-ip>
```

The `-k` flag is required here: the machine does not trust the gateway's certificate yet, which is what the script installs. Run this only over a network path you control, or copy the script across yourself and check it first.

Alternatively, point an individual Inference Server at the gateway with the `SECURE_GATEWAY` environment variable; see [Connecting Inference Servers](/deployment/self-hosted/enterprise/secure-gateway.md#connecting-inference-servers). Use `SECURE_GATEWAY=https://repo.roboflow.com`. Use the explicit HTTPS scheme across versions; the pending runtime-hardening build changes how bare addresses and plaintext gateways are handled, as described in [Security Configuration Migration](/deployment/self-hosted/inference-server/configuration/security-migration.md#gateway-transport). The hostname matters too: the generated certificate covers `repo.roboflow.com` and not the host's IP address, so `https://<host-ip>` fails certificate validation even after the machine trusts the certificate. Run the client install script first, which points `repo.roboflow.com` at the gateway in `/etc/hosts` and installs the certificate. To use the IP address instead, issue your own certificate covering it and install it with `--cert` and `--key`. Only the gateway needs outbound access to `api.roboflow.com` and `repo.roboflow.com`.

## Troubleshooting

<table data-search="false"><thead><tr><th>Symptom</th><th>What to do</th></tr></thead><tbody><tr><td>Containers cannot resolve each other by name, and the gateway logs name-resolution errors reaching its cache</td><td>Podman is using the legacy CNI network backend, which has no container DNS, usually because of CNI leftovers from an earlier container runtime. Check with <code>sudo podman info --format '{{.Host.NetworkBackend}}'</code>; it should report <code>netavark</code>. The installer writes a drop-in under <code>/etc/containers/containers.conf.d/</code> to select netavark when needed. Existing containers stay on the old backend after the fix, so remove them with <code>sudo podman rm -f secure-gateway seaweedfs</code> and restart the Deployment Manager (<code>sudo systemctl restart rfdm</code>) to recreate them.</td></tr><tr><td>Gateway fails with <code>Permission denied</code> reading its TLS key</td><td>The certificate and key files must be owned <code>1000:0</code> (mode <code>0640</code> on the key, <code>0644</code> on the certificate), in a directory owned <code>root:0</code> with mode <code>0750</code>. The installer sets this; the usual cause is a certificate replaced manually as root. Set them by hand, then restart the gateway container: <code>sudo chown 1000:0 /etc/secure-gateway/tls/tls.crt /etc/secure-gateway/tls/tls.key</code>, <code>sudo chmod 0640 /etc/secure-gateway/tls/tls.key</code>, <code>sudo chmod 0644 /etc/secure-gateway/tls/tls.crt</code>. Re-running <code>install-podman.sh</code> also fixes it, but pass <code>--cert</code> and <code>--key</code> again if you brought your own certificate, or the re-run replaces it with a self-signed one.</td></tr><tr><td><code>Permission denied</code> writing to a mounted directory</td><td>The volume mount is missing its SELinux label. Every volume in the managed configuration carries a <code>:Z</code> (or shared <code>:z</code>) option; if you added a mount by hand, add the label rather than putting SELinux in permissive mode.</td></tr><tr><td>Gateway crash-loops with <code>PermissionError</code> reading <code>/etc/ssl/certs/ca-certificates.crt</code></td><td>SELinux is blocking the container from the host CA bundle the Deployment Manager mounts in. The denial is suppressed, so audit searches turn up nothing. Run <code>sudo setsebool -P container_read_certs on</code>. The installer sets this boolean, so re-running it fixes the host as well.</td></tr><tr><td>Gateway logs <code>Init handshake failed ... starting in local-cache-only mode</code></td><td>The gateway could not reach the Roboflow API at startup (wrong API key, no outbound connectivity, or the cache was still starting). It keeps serving traffic and still caches locally, but runs without its S3-backed cache and cannot report telemetry or events to the backend until the next restart. Fix the cause, then restart the gateway container: <code>sudo podman restart &#x3C;gateway-container></code>, with the container name from <code>sudo podman ps</code>.</td></tr><tr><td>Where the logs are</td><td>Deployment Manager: <code>sudo journalctl -u rfdm</code>. Gateway and cache: <code>sudo podman logs &#x3C;container></code>, with the container names from <code>sudo podman ps</code>.</td></tr></tbody></table>

## Support matrix

<table data-search="false"><thead><tr><th>RHEL</th><th>Podman</th><th>Notes</th></tr></thead><tbody><tr><td>9.x</td><td>4.x</td><td>Podman 4 ships with RHEL 9</td></tr><tr><td>9.x</td><td>5.x</td><td>Validated on RHEL 9.6 with Podman 5.4.0</td></tr></tbody></table>

Podman 4.x is the minimum supported version. The Podman that ships with a current RHEL 9.x release is supported without any additional repositories or packages.
