Skip to content

Instantly share code, notes, and snippets.

@heri16
Last active July 12, 2025 00:27
Show Gist options
  • Save heri16/60acf5b57518cd8518e62f1ce74f14a8 to your computer and use it in GitHub Desktop.
Save heri16/60acf5b57518cd8518e62f1ce74f14a8 to your computer and use it in GitHub Desktop.
Setup gVisor for rootless podman on Fedora CoreOS (fcos)
#!/bin/bash
# Setup podman virtual machine with no bind mounts: `podman machine init -v ''`.
# Start podman virtual machine: `podman machine start`.
# Connect to podman machine: `podman machine ssh`.
# Install latest release of gVisor
# See: https://gvisor.dev/docs/user_guide/install/
(
set -e
ARCH=$(uname -m)
URL=https://storage.googleapis.com/gvisor/releases/release/latest/${ARCH}
curl --proto '=https' --tlsv1.3 -O "${URL}/runsc" \
-O "${URL}/runsc.sha512" \
-O "${URL}/containerd-shim-runsc-v1" \
-O "${URL}/containerd-shim-runsc-v1.sha512"
sha512sum -c runsc.sha512 \
-c containerd-shim-runsc-v1.sha512
rm -f *.sha512
chmod a+rx runsc containerd-shim-runsc-v1
mkdir -p ${HOME}/.local/bin
mv runsc containerd-shim-runsc-v1 ${HOME}/.local/bin
)
# Run a test container with gVisor
podman run --runtime=${HOME}/.local/bin/runsc \
--runtime-flag ignore-cgroups \
--security-opt label=disable \
--rm alpine sh -c 'dmesg && echo hello world'
# Create wrapper for gVisor's runsc for rootless podman
# as containers.conf do not support defining default runtime flags or args
cat > "${HOME}/.local/bin/runsc-rootless" << 'EOF'
#!/bin/sh
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# TODO: https://github.com/containers/podman/issues/25565 - Use --runtime-flag
# in containers.conf and delete this wrapper.
# TODO: https://github.com/google/gvisor/issues/11543 - Remove -ignore-cgroups
# TODO: https://github.com/google/gvisor/issues/10359 - Remove -network=host
# TODO: https://github.com/google/gvisor/issues/11794 - Remove -overlay2=none
set -euo pipefail
# Filter out unsupported flags
args=()
while [ "$#" -gt 0 ]; do
case "$1" in
--no-new-keyring)
shift ;; # skip this flag
create)
args+=("--ignore-cgroups")
args+=("$1")
shift ;;
*)
args+=("$1")
shift ;;
esac
done
exec "$(dirname "$0")/runsc" \
--host-uds=open \
"${args[@]}"
EOF
chmod a+rx "${HOME}/.local/bin/runsc-rootless"
# The wrapper enables access to host's UNIX domain sockets (only required for devcontainer's bootstrap)
# See: `runsc flags`
# Enable `docker buildx build` (only required for devcontainers with Dockerfile)
# See: https://github.com/moby/buildkit/blob/master/docs/rootless.md#docker
# podman run --replace --name buildx_buildkit_default --runtime=crun --privileged -v buildx_buildkit_default_state:/var/lib/buildkit:rw,rprivate -d --restart=unless-stopped moby/buildkit:buildx-stable-1 --rootless --oci-worker-rootless
podman run --replace --name buildx_buildkit_default \
--runtime=crun \
--security-opt seccomp=unconfined \
--security-opt apparmor=unconfined \
--security-opt systempaths=unconfined \
-v buildx_buildkit_default_state:/var/lib/buildkit:rw,rprivate \
-d --restart=unless-stopped moby/buildkit:rootless
# Register gVisor as default runtime (for rootless docker)
mkdir -p "${HOME}/.config/docker"
tee "${HOME}/.config/docker/daemon.json" << EOF
{
"selinux-enabled": false,
"runtimes": {
"runsc": {
"path": "${HOME}/.local/bin/runsc",
"runtimeArgs": [
"-ignore-cgroups",
"-host-uds=open"
]
}
},
"default-runtime": "runsc"
}
EOF
# Register gVisor as default runtime (for rootless podman)
tee -a "${HOME}/.config/containers/containers.conf" << EOF
label = false
[engine.runtimes]
runsc = [
"${HOME}/.local/bin/runsc-rootless",
]
[engine]
runtime = "runsc"
EOF
# Make buildah (podman build) always run using gVisor
tee -a "${HOME}/.bash_profile" << EOF
export BUILDAH_RUNTIME=${HOME}/.local/bin/runsc-rootless
export BUILDAH_ISOLATION=chroot
EOF
# Run a test container with rootless gVisor
podman run --rm alpine sh -c 'dmesg && echo hello world'
# List containers using gVisor's userspace-kernel and runsc runtime
runsc list
# List all containers and their runtimes
podman ps -a --format "table {{.ID}}\t{{.Names}}" | while read id name; do runtime=$(podman inspect --format '{{.OCIRuntime}}' "$id"); echo -e "$id\t$name\t${runtime:-Runtime}"; done
# Test a containerized web service with gVisor
sudo podman run --runtime=$(command -v runsc) --security-opt label=disable -p 3003:3003 --rm -it alpine sh
apk add --no-cache curl libstdc++
PATH="${HOME}/.local/bin:${PATH}"
curl --proto '=https' --tlsv1.3 https://mise.run | sh
eval "$(mise activate --shims)"
mise use -g bun@latest
bun init --react ./app && cd ./app
env PORT=3003 bun dev
exit
# Remote Browser Isolation with gVisor
# Starts Firefox browser at https://localhost:3001
sudo mkdir -p /var/home/firefox
sudo podman run -d \
--name=firefox --replace \
--runtime=$(command -v runsc) \
--security-opt label=disable \
-e PUID=$(id -u) \
-e PGID=$(id -g) \
-e TZ=Etc/UTC \
-e FIREFOX_CLI=https://youtube.com \
-e MOZ_FAKE_NO_SANDBOX=1 \
-p 3000:3000 \
-p 3001:3001 \
-v /var/home/firefox:/config \
--shm-size="1gb" \
--restart unless-stopped \
lscr.io/linuxserver/firefox:latest
exit
# Useful commands to manage Isolated Firefox Browser
podman --connection podman-machine-default-root stats --no-stream
podman --connection podman-machine-default-root pause firefox
podman --connection podman-machine-default-root unpause firefox
podman --connection podman-machine-default-root restart firefox
podman --connection podman-machine-default-root logs -f firefox