Running the Gateway as a Container

View as Markdown

Use this approach when you want to run the OpenShell gateway as a container instead of installing it with the system package manager. This is useful on immutable OS distributions (Fedora CoreOS, bootc-based images, Silverblue) where the standard installer is not appropriate, or anywhere you prefer a container-first workflow.

The gateway image is published at ghcr.io/nvidia/openshell/gateway.

Prerequisites for the Docker Driver

When the gateway runs as a container and creates Docker-backed sandboxes, the gateway container communicates with the host Docker daemon via the mounted socket. This requires three things beyond a basic docker run:

  1. Docker socket access. The gateway process must be able to read and write the Docker socket. Add the docker group (or the GID of /var/run/docker.sock) so the socket is accessible without running as root.

  2. gRPC endpoint. Sandbox containers call back to the gateway over the OPENSHELL_GRPC_ENDPOINT address. The Docker driver substitutes host.openshell.internal as the host and the gateway’s own bind port as the port — only the scheme (http or https) is preserved. Use http://host.openshell.internal:8080 when TLS is disabled and https://host.openshell.internal:8080 when mTLS is enabled. The docker driver automatically binds the gateway to the bridge network interface so sandbox containers can reach it — you do not need to expose the port on 0.0.0.0.

  3. Supervisor binary on the host. The gateway bind-mounts the openshell-sandbox supervisor binary into each sandbox container. Because bind-mount paths are resolved by the host Docker daemon (not inside the gateway container), the binary must exist at a path on the host filesystem and be mounted at the same absolute path inside the gateway container. That way the path the gateway records internally matches what Docker can find on the host when it creates sandbox containers.

Quick Start

Extract the supervisor binary to the host once, then start the gateway:

$mkdir -p ~/openshell/supervisor
$docker create --name tmp-supervisor ghcr.io/nvidia/openshell/supervisor:latest
$docker cp tmp-supervisor:/openshell-sandbox ~/openshell/supervisor/openshell-sandbox
$docker rm tmp-supervisor
$chmod +x ~/openshell/supervisor/openshell-sandbox

Start the gateway:

$docker run -d \
> --name openshell-gateway \
> --restart unless-stopped \
> --group-add docker \
> -p 127.0.0.1:8080:8080 \
> -v openshell-state:/var/openshell \
> -v /var/run/docker.sock:/var/run/docker.sock \
> -v ~/openshell/supervisor/openshell-sandbox:~/openshell/supervisor/openshell-sandbox:ro \
> -e OPENSHELL_DRIVERS=docker \
> -e OPENSHELL_GRPC_ENDPOINT=http://host.openshell.internal:8080 \
> -e OPENSHELL_DOCKER_SUPERVISOR_BIN=~/openshell/supervisor/openshell-sandbox \
> -e OPENSHELL_DB_URL=sqlite:/var/openshell/openshell.db \
> -e OPENSHELL_DISABLE_TLS=true \
> ghcr.io/nvidia/openshell/gateway:latest

The volume mount uses ~/openshell/supervisor/openshell-sandbox for both the host and container paths. The shell expands ~ in both halves before passing the argument to Docker, so both sides resolve to the same absolute path (e.g., /home/user/openshell/supervisor/openshell-sandbox). This satisfies the same-path requirement so the host Docker daemon can find the binary when creating sandbox containers.

Register the gateway with the CLI. If running on the same machine, use --local:

$openshell gateway add http://127.0.0.1:8080 --local --name local

If registering from a different machine on the same network, use the host IP and --remote:

$openshell gateway add http://HOST_IP:8080 --remote --name remote

Confirm the CLI can reach the gateway:

$openshell status

Disabling TLS removes authentication. This example binds to 127.0.0.1 so only local connections are accepted. To accept remote connections, enable mTLS or restrict access with a firewall rule.

Full mTLS Setup

To run the gateway with mutual TLS, generate the PKI bundle first, then start the gateway with the cert paths configured.

Bootstrap the PKI into a local state directory:

$mkdir -p ~/.local/state/openshell/tls
$
$docker run --rm \
> -v "$HOME/.local/state/openshell:/home/openshell/.local/state/openshell" \
> -v "$HOME/.config/openshell:/home/openshell/.config/openshell" \
> ghcr.io/nvidia/openshell/gateway:latest \
> generate-certs \
> --output-dir /home/openshell/.local/state/openshell/tls \
> --server-san host.openshell.internal

This writes the server and client certificates under ~/.local/state/openshell/tls/, writes sandbox JWT signing keys under ~/.local/state/openshell/tls/jwt/, and copies the client bundle to ~/.config/openshell/gateways/openshell/mtls/ so the CLI picks it up automatically.

Start the gateway with mTLS enabled:

$docker run -d \
> --name openshell-gateway \
> --restart unless-stopped \
> --group-add docker \
> -p 127.0.0.1:8080:8080 \
> -v "$HOME/.local/state/openshell:/home/openshell/.local/state/openshell" \
> -v /var/run/docker.sock:/var/run/docker.sock \
> -v ~/openshell/supervisor/openshell-sandbox:~/openshell/supervisor/openshell-sandbox:ro \
> -e OPENSHELL_DRIVERS=docker \
> -e OPENSHELL_GRPC_ENDPOINT=https://127.0.0.1:8080 \
> -e OPENSHELL_DOCKER_SUPERVISOR_BIN=~/openshell/supervisor/openshell-sandbox \
> -e OPENSHELL_DB_URL=sqlite:/home/openshell/.local/state/openshell/openshell.db \
> -e OPENSHELL_LOCAL_TLS_DIR=/home/openshell/.local/state/openshell/tls \
> -e OPENSHELL_TLS_CERT=/home/openshell/.local/state/openshell/tls/server/tls.crt \
> -e OPENSHELL_TLS_KEY=/home/openshell/.local/state/openshell/tls/server/tls.key \
> -e OPENSHELL_TLS_CLIENT_CA=/home/openshell/.local/state/openshell/tls/ca.crt \
> -e OPENSHELL_ENABLE_MTLS_AUTH=true \
> -e OPENSHELL_DOCKER_TLS_CA=/home/openshell/.local/state/openshell/tls/ca.crt \
> -e OPENSHELL_DOCKER_TLS_CERT=/home/openshell/.local/state/openshell/tls/client/tls.crt \
> -e OPENSHELL_DOCKER_TLS_KEY=/home/openshell/.local/state/openshell/tls/client/tls.key \
> ghcr.io/nvidia/openshell/gateway:latest

Register the gateway with mTLS:

$openshell gateway add https://127.0.0.1:8080 --local --name local

Docker Compose

The deploy/docker/ directory in the repository contains a production-ready Compose setup with full inline documentation:

FilePurpose
docker-compose.ymlGateway service, volumes, and environment variables
gateway.tomlTOML configuration mounted into the container

Clone or copy those files, then start the gateway:

$docker compose -f deploy/docker/docker-compose.yml up -d

Register the gateway with the CLI. If registering from the same machine:

$openshell gateway add http://127.0.0.1:8080 --local --name local

If registering from a different machine on the same network, replace HOST_IP with the machine’s LAN address:

$openshell gateway add http://HOST_IP:8080 --remote --name remote

Using Podman

Replace docker with podman in the commands above. Mount the Podman socket instead of the Docker socket and set the driver to podman:

$podman run -d \
> --name openshell-gateway \
> -p 127.0.0.1:8080:8080 \
> -v openshell-state:/var/openshell \
> -v "$XDG_RUNTIME_DIR/podman/podman.sock:/var/run/podman.sock" \
> -e OPENSHELL_DRIVERS=podman \
> -e OPENSHELL_PODMAN_SOCKET=/var/run/podman.sock \
> -e OPENSHELL_DB_URL=sqlite:/var/openshell/openshell.db \
> -e OPENSHELL_DISABLE_TLS=true \
> ghcr.io/nvidia/openshell/gateway:latest

Next Steps