railcar

repository·master·Indexed Aug 2, 2026

https://github.com/oracle/railcar

A memory-safe Rust implementation of the Open Container Initiative (OCI) runtime specification designed as an alternative to runc. It provides a CLI for running containers from OCI bundles and can be integrated as a Docker runtime backend. The project includes libraries for managing OCI spec files, Linux process capabilities, cgroup-based resource limits, and Seccomp profiles.

Tokens
6.8K
Snippets
30
Records
41
Agent score
77%

What's inside railcar

  1. Install and build `railcar`

    master

    To build railcar from source, you must first install the Rust toolchain and its dependencies. You can build either a dynamic binary (default) or a static binary using musl.

    Rust Setup

    Install Rust and the necessary toolchains:

    curl https://sh.rustup.rs -sSf | sh
    cargo install cargo-when
    
    # For stable builds
    rustup toolchain install stable-x86_64-unknown-linux-gnu
    rustup default stable-x86_64-unknown-linux-gnu
    rustup target install x86_64-unknown-linux-musl
    
    # For nightly builds
    rustup toolchain install nightly-x86_64-unknown-linux-gnu
    rustup default nightly-x86_64-unknown-linux-gnu
    rustup target install x86_64-unknown-linux-musl

    Building

    Use the provided build.sh script to compile the project.

    • Dynamic build (default): Requires libseccomp-devel.
    • Static build: Requires git submodule update --init, autotools, make, gcc, and musl-gcc. Set the TARGET environment variable to x86_64-unknown-linux-musl.
    • Release build: Use the --release flag.

    Note: If you build using the stable toolchain instead of nightly, the set_name feature is disabled, meaning the container's init process will not be named rc-init in ps or /proc/$pid/cmdline.

    # Build a static binary
    TARGET=x86_64-unknown-linux-musl ./build.sh
    
    # Build a release version
    build.sh --release
  2. Use `railcar` to run containers

    master

    You can use the railcar binary to run containers by specifying the run command. If your OCI bundle (containing config.json) is not in the current directory, use the -b flag to specify the bundle directory.

    # Run a container from the current directory
    ./railcar run
    
    # Run a container from a specific bundle directory
    ./railcar -b /some/other/directory run
    ./railcar run
  3. Use `railcar` as a Docker runtime backend

    master

    You can integrate railcar into Docker by adding it as a runtime to the Docker daemon.

    1. Start the Docker daemon with the --experimental flag and use --add-runtime to map a name (e.g., rc) to the path of the railcar binary.
    2. Run containers using the --runtime flag to specify the rc backend.

    Important: When starting the daemon, use the -t option so that Docker can properly collect stdout and stderr from railcar. To run a container in the background (daemonized), use the -d flag with Docker.

    # 1. Start dockerd with railcar backend
    dockerd ... --experimental --add-runtime "rc=/path/to/railcar"
    
    # 2. Run a container using the rc runtime
    docker run -it --rm --runtime rc hello
    
    # To run in the background
    docker run -dt --rm --runtime rc hello
  4. How railcar manages namespaces and isolation

    master

    railcar implements OCI runtime isolation by leveraging Linux namespaces. During the container setup lifecycle, it performs the following operations:

    1. Namespace Collection: It reads the linux.namespaces configuration from the OCI spec. If a namespace path is provided, it opens a file descriptor for that path to use with setns. If no path is provided, it uses unshare with the corresponding CloneFlags (e.g., CLONE_NEWPID, CLONE_NEWNS, CLONE_NEWUSER).
    2. User Namespace Setup: If CLONE_NEWUSER is requested, railcar handles UID/GID mapping by writing to /proc/[pid]/uid_map and /proc/[pid]/gid_map to map container IDs to host IDs.
    3. Mount Namespace & Rootfs: For CLONE_NEWNS, railcar initializes the root filesystem using mounts::init_rootfs, performs a pivot_rootfs, and applies sysctls within the new mount namespace.
    4. PID Namespace: If CLONE_NEWPID is requested, railcar performs an additional fork to ensure the container process becomes PID 1 in the new namespace.
    5. UTS Namespace: If CLONE_NEWUTS is present, the process hostname is set via sethostname using the value from the spec.
  5. How railcar manages cgroups and resource limits

    master

    railcar applies resource constraints using Linux cgroups and rlimits:

    1. Cgroups: The cgroup path is determined by linux.cgroups_path in the spec, or defaults to / + container_id. The cgroups::apply function is used to apply the requested resources to the container process.
    2. Rlimits: Resource limits (rlimits) defined in spec.process.rlimits are applied to the child process immediately after forking, before entering user namespaces.
    3. OOM Score: If linux.resources.oom_score_adj is specified, railcar writes the adjustment value to /proc/self/oom_score_adj to control the Out-Of-Memory killer behavior.
  6. Configure Linux process capabilities

    master

    The LinuxCapabilities struct allows you to define the capability sets for a Linux process. It supports the following sets:

    • bounding: The bounding set of capabilities.
    • effective: The effective capability set.
    • inheritable: The inheritable capability set.
    • permitted: The permitted capability set.
    • ambient: The ambient capability set.

    Capabilities are defined using the LinuxCapabilityType enum (e.g., CAP_CHOWN, CAP_NET_ADMIN, CAP_SYS_ADMIN).

  7. Configure Linux Seccomp profiles

    master

    The LinuxSeccomp struct allows you to define a syscall filter. It includes:

    • default_action: The LinuxSeccompAction to take if no syscall matches (e.g., SCMP_ACT_ALLOW, SCMP_ACT_KILL).
    • architectures: A list of supported Arch values.
    • syscalls: A list of LinuxSyscall definitions, where each syscall can specify an action and arguments with specific operators (e.g., SCMP_CMP_EQ, SCMP_CMP_GT).
  8. How railcar handles container lifecycle hooks

    master

    railcar executes lifecycle hooks defined in the OCI runtime specification at specific stages of the container creation process. Hooks are executed by the parent process after the container environment has been partially or fully set up.

    • Prestart Hooks: Executed after the container environment is set up (namespaces, cgroups, etc.) but before the container process is actually started. The hook receives a state object containing the container ID, status (running), the init PID, and the rootfs path.
    • Poststart Hooks: Executed after the container process has been started. These are treated as non-critical; if a poststart hook fails, railcar logs a warning but continues the execution flow.
  9. Configure Linux resource limits (cgroups)

    master

    The LinuxResources struct is used to configure cgroup-based resource limits for a container. It includes support for:

    • memory: Memory limits, reservations, and swap.
    • cpu: CPU shares, quota, and period.
    • pids: PID limits.
    • block_io: Block I/O weights and throttling.
    • devices: Device cgroup allow/deny rules.
    • hugepage_limits: Limits on hugepage usage.
    • network: Network interface priorities.
    • disable_oom_killer: Boolean to disable the OOM killer.
    • oom_score_adj: Adjustment for the OOM killer score.
  10. How railcar handles process privileges and security

    master

    railcar implements security boundaries through several mechanisms:

    • No New Privileges: If spec.process.no_new_privileges is true, railcar calls prctl::set_no_new_privileges(true) to prevent processes from gaining new privileges via setuid binaries.
    • Capabilities: railcar can drop specific Linux capabilities as defined in spec.process.capabilities using capabilities::drop_privileges.
    • Seccomp: If seccomp profiles are provided in linux.seccomp, railcar initializes them using seccomp::initialize_seccomp.
    • User IDs/GIDs: The process identity is set using setid(uid, gid), which involves setting the real, effective, and saved IDs. If the user is not root (UID 0), railcar ensures effective capabilities are reset.
  11. Supported and unsupported `railcar` commands

    master

    While railcar is a Rust implementation of the OCI runtime spec and is similar to runc, it does not support all runc commands.

    Currently unsupported commands:

    • checkpoint
    • events
    • exec
    • init
    • list
    • pause
    • restore
    • resume
    • spec

    Key behavioral difference: railcar always runs an init process separately from the container process.

  12. Use `railcar run` to execute a container

    master

    The run command is the quickest way to start a container. It requires a unique identifier (id) and a bundle directory containing the OCI config.json.

    Options for run:

    • id: Unique identifier for the container.
    • -b, --bundle: Directory containing config.json (defaults to .).
    • -p, --pid-file: Additional location to write the PID.
    • -n, --no-init: Do not create an init process.
    # Example: Run a container from the current directory
    railcar run my-container --bundle .
    
    # Example: Run a container with a specific PID file and no init process
    railcar run my-container --bundle ./my-bundle --pid-file /tmp/my-container.pid --no-init