> 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-microshift.md).

# Secure Gateway on MicroShift

Deploy Secure Gateway on Red Hat Device Edge, with the gateway and its encrypted cache running as MicroShift workloads managed by Roboflow Deployment Manager.

[Secure Gateway](/deployment/self-hosted/enterprise/secure-gateway.md) also runs on Red Hat Device Edge using MicroShift, Red Hat's Kubernetes distribution for edge devices. A dedicated installer deploys the gateway and its encrypted cache as Kubernetes workloads, supervised by the Roboflow Deployment Manager (`rfdm`) running on the RHEL host. 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) packaged for hosts standardized on Red Hat Device Edge.

{% hint style="info" %}
This page covers the MicroShift deployment. For RHEL hosts without MicroShift or Docker, see [Secure Gateway on RHEL with Podman](/deployment/self-hosted/enterprise/secure-gateway-podman.md). For Docker hosts, and for the gateway's configuration reference (caching, TLS variables, log export), 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 RHEL host as a systemd service and manages workloads through MicroShift's Kubernetes API. 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 Deployments in the `roboflow-edge` namespace.
* A `LoadBalancer` Service publishes the gateway on the device IP: HTTPS on port `443` and an HTTP-to-HTTPS redirect on port `80`.
* The cache is stored on a persistent volume provisioned by MicroShift's LVMS storage provisioner. The volume claim is 60 GB, sized for the gateway's 50 GB cache plus headroom.
* All workloads run unprivileged under MicroShift's default restricted security profile: no privileged containers, no root users, and no custom security context grants.

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

## Prerequisites

* A Red Hat Enterprise Linux 9.6 host on x86\_64, with a valid Red Hat subscription. The installer bundle is built for `linux-amd64` only.
* Red Hat build of MicroShift 4.16 or newer, installed and running with your OpenShift pull secret configured. 4.20 (EUS) is recommended; see the [support matrix](#support-matrix).
* An LVM volume group with at least 60 GB free. MicroShift's LVMS provisioner creates the gateway's cache volume from it; with no volume group, or too little free space, the storage request stays `Pending` and the cache never starts.
* `skopeo` installed on the host (`sudo dnf install skopeo`). The installer copies the bundled images into CRI-O's storage with it and stops if it is missing.
* The OpenShift CLI (`oc`) installed on the host, at a version matching your MicroShift release. MicroShift does not install it, and the verification and troubleshooting commands on this page use it. Install it somewhere on sudo's `secure_path` (`/usr/bin` works): `sudo` drops `/usr/local/bin` from `PATH`, so an `oc` unpacked there gives `sudo: oc: command not found`.
* Ports `80` and `443` available for the gateway on the device IP
* For connected installs, outbound HTTPS to `api.roboflow.com` and `repo.roboflow.com`

If firewalld is running, which it is on a stock RHEL installation, open the gateway's ports and put MicroShift's pod network in the trusted zone before you install. Without this, pod networking and client access both fail:

```bash
sudo firewall-cmd --permanent --zone=trusted --add-source=10.42.0.0/16
sudo firewall-cmd --permanent --zone=trusted --add-source=169.254.169.1
sudo firewall-cmd --permanent --add-service=http --add-service=https
sudo firewall-cmd --reload
```

A firewalld reload leaves MicroShift pod networking broken until the OVN pods restart, so follow the reload with the recovery step in [Troubleshooting](#troubleshooting).

{% hint style="warning" %}
The installer disables MicroShift's default ingress router so the gateway can own ports 80 and 443 on the device. If you already serve other workloads through the router on this host, this deployment model conflicts with them: use a dedicated device for the gateway.
{% endhint %}

## Install on a connected host

Download the MicroShift installer bundle and verify its integrity:

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

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

Extract it and run the installer as root:

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

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

The installer:

1. Checks that MicroShift is running and at a supported version, and that an LVM volume group is available for storage.
2. Loads the bundled Secure Gateway and SeaweedFS images into the host's container storage. Nothing is pulled from a registry.
3. Pins the images in CRI-O's configuration so Kubernetes disk-pressure garbage collection never removes them.
4. Disables MicroShift's default ingress router and restarts MicroShift, freeing ports 80 and 443 for the gateway.
5. Installs the Roboflow Deployment Manager as a systemd service and writes its device configuration.
6. Starts the Deployment Manager, which creates the `roboflow-edge` namespace and deploys the gateway and cache.

Verify the deployment:

```bash
sudo systemctl is-active rfdm
sudo oc get pods -n roboflow-edge \
  --kubeconfig /var/lib/microshift/resources/kubeadmin/kubeconfig
curl -fk https://<device-ip>/health
```

Both pods should be `Running`, 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 container storage. The workloads 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 device by removable media or your internal file transfer process, then extract and run `install-microshift.sh` exactly as above. Verification is the same.

## TLS and trust

By default, the Roboflow Deployment Manager generates a self-signed certificate for the gateway and stores it in the `secure-gateway-tls` Secret (type `kubernetes.io/tls`) in the `roboflow-edge` namespace. The gateway pod mounts this Secret and serves HTTPS on port 443. The certificate survives Deployment Manager reinstalls. The Deployment Manager replaces it when it is missing, when it is within 30 days of expiry, when the certificate and private key no longer match, or when it no longer covers every name a fresh certificate would carry.

Your own certificate has to cover all of those names, or the Deployment Manager replaces it with a self-signed one on the next reconcile:

* `repo.roboflow.com`, `*.roboflow.com`, and `localhost`. Clients reach the gateway under `repo.roboflow.com`, so container image pulls fail if the certificate does not cover it.
* `secure-gateway`, `secure-gateway.roboflow-edge.svc`, and `secure-gateway.roboflow-edge.svc.cluster.local`
* The device's hostname
* The IP addresses `127.0.0.1` and the device IP

To use your own certificate, replace the Secret's contents and restart the gateway:

```bash
export KUBECONFIG=/var/lib/microshift/resources/kubeadmin/kubeconfig
sudo -E oc create secret tls secure-gateway-tls -n roboflow-edge \
  --cert=/path/to/gateway.crt --key=/path/to/gateway.key \
  --dry-run=client -o yaml | sudo -E oc apply -f -
sudo -E oc rollout restart deployment/secure-gateway -n roboflow-edge
```

Renew your certificate more than 30 days before it expires. If the certificate in the Secret enters its final 30 days, the Deployment Manager replaces it with a freshly generated self-signed one. Changing the device's IP address or hostname has the same effect, so reissue your certificate before you change either.

The Deployment Manager also publishes a trust bundle as the `roboflow-trust-bundle` ConfigMap, mounted into the gateway. The gateway uses it to trust TLS certificates presented by other Roboflow-managed devices in your fleet, so devices can authenticate to the gateway without you distributing certificates by hand.

## 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://<device-ip>/install-client.sh | sudo bash -s -- --server <device-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 the explicit scheme (`SECURE_GATEWAY=https://<device-ip>`) across versions; the certificate must cover that IP. The pending runtime-hardening build changes bare-address and plaintext behavior, as described in [Security Configuration Migration](/deployment/self-hosted/inference-server/configuration/security-migration.md#gateway-transport). The Inference Server also has to trust the gateway's certificate, which the client install script handles for you. 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>Cache volume stuck <code>Pending</code></td><td>No LVM volume group, or none with 60 GB free. Check free space with <code>sudo vgs</code>; once a volume group has the capacity, the volume binds automatically. To run on a smaller volume, lower <code>storage_size</code> and <code>CACHE_MAX_SIZE_GB</code> together in <code>/opt/rfdm/config/rfconfig.json</code>.</td></tr><tr><td>Gateway <code>LoadBalancer</code> Service stuck <code>&#x3C;pending></code></td><td>Ports 80/443 on the device are still held, usually because the default router is still enabled. The installer sets this through a snippet at <code>/etc/microshift/config.d/10-roboflow-ingress.yaml</code>, not in <code>/etc/microshift/config.yaml</code>. Confirm <code>sudo microshift show-config --mode effective</code> reports <code>ingress.status: Removed</code>, restore the snippet if it is gone, and restart MicroShift; also check no other host process binds 80 or 443.</td></tr><tr><td>Connections fail after a firewalld reload</td><td>Reloading firewalld breaks MicroShift pod networking until the OVN networking pods restart. Delete the pods in the <code>openshift-ovn-kubernetes</code> namespace so they are recreated, or restart MicroShift.</td></tr><tr><td>Node stays <code>NotReady</code>; <code>ovnkube-master</code> pod crash-loops with <code>MTU (...) of network interface ... is too small for specified overlay MTU (1500)</code></td><td>Your network interface MTU is below 1500 (common on cloud VPCs and VPN links; GCP uses 1460). Set the pod MTU to the interface MTU minus 100 in <code>/etc/microshift/ovn.yaml</code>: for a 1460 interface, the file should contain the single line <code>mtu: 1360</code>. Then run <code>sudo systemctl stop microshift</code>, <code>echo 1 | sudo microshift-cleanup-data --ovn</code>, and <code>sudo systemctl start microshift</code>. The cleanup step is required because the previous MTU is stored in the OVN database.</td></tr><tr><td>Gateway pods stuck <code>ImagePullBackOff</code> after host image cleanup</td><td>Image pinning protects the exact bundled references, but <code>podman rmi</code> or <code>podman image prune</code> on the host can still remove sibling images and shared layers from the same storage CRI-O uses. Avoid pruning container storage on the device; if the images are gone, re-run the installer to reload them.</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 run <code>sudo oc rollout restart deployment/secure-gateway -n roboflow-edge --kubeconfig /var/lib/microshift/resources/kubeadmin/kubeconfig</code>.</td></tr><tr><td>Where the logs are</td><td>Deployment Manager: <code>sudo journalctl -u rfdm</code>. Gateway and cache: <code>sudo oc logs deployment/secure-gateway -n roboflow-edge</code> (and <code>deployment/seaweedfs</code>), with <code>--kubeconfig /var/lib/microshift/resources/kubeadmin/kubeconfig</code>.</td></tr></tbody></table>

## Support matrix

<table data-search="false"><thead><tr><th>MicroShift</th><th>RHEL</th><th>Notes</th></tr></thead><tbody><tr><td>4.19</td><td>9.6</td><td></td></tr><tr><td>4.20 (EUS)</td><td>9.6</td><td>Recommended</td></tr><tr><td>4.21</td><td>9.6</td><td>MicroShift 4.21 on RHEL 10 is a Red Hat Technology Preview and is not supported for Secure Gateway.</td></tr></tbody></table>

MicroShift 4.16 is the minimum supported version. Even-numbered MicroShift releases carry Extended Update Support (EUS), so 4.20 is the best choice for long-lived edge deployments.
