A minimal example demonstrating libcrun-go by running a statically compiled binary in a container.
Instead of using a full rootfs (like busybox or alpine), this example uses a single statically compiled Go binary. The example:
- Creates a temporary directory in
/tmpfor the rootfs - Copies the static binary into it
- Creates a minimal
/etc/passwd(required by libcrun to detect HOME) - Sets the
HOMEenvironment variable - Runs the container using that minimal rootfs
- Cleans up the temporary directory on exit
The static binary has no external dependencies, so it can run in an empty filesystem with just itself.
- Linux (libcrun only works on Linux)
- Go 1.25+
- libcrun dependencies installed:
libsystemd-dev,libseccomp-dev,libcap-dev - Root privileges or properly configured rootless containers
First, compile the static hello world binary:
cd static-helloworld
make
cd ..This creates a helloworld binary (~1.8MB) with no external dependencies.
Run as root:
sudo go run main.goOr build and run:
go build -o helloworld-example main.go
sudo ./helloworld-exampleFound static binary: /path/to/static-helloworld/helloworld
Using rootfs: /tmp/crun-rootfs-XXXXXX
Running container...
========== CONTAINER RESULTS ==========
Exit code: 0
--- STDOUT (30 bytes) ---
Hello, World from container!
--- STDERR (0 bytes) ---
(empty)
========================================
To enable verbose libcrun logging, set DEBUG=1:
sudo DEBUG=1 go run main.goThis enables debug verbosity and sets up a custom log handler that captures libcrun's internal logs. You'll see messages like:
[libcrun:DEBUG] Running linux container
[libcrun:DEBUG] Unsharing namespace: `pid`
[libcrun:DEBUG] Joining `network` namespace: `/proc/1/ns/net`
[libcrun:WARN] cannot detect HOME environment variable, setting default
This shows detailed information about namespace setup, cgroup configuration, and container execution.
The example runs in rootless mode by default. For rootless containers to work, you may need:
- User namespaces enabled (
/proc/sys/kernel/unprivileged_userns_clone= 1) - Subuid/subgid mappings configured in
/etc/subuidand/etc/subgid
If rootless doesn't work, running with sudo is the simplest alternative.