How to Configure Read-Only Root Filesystems and tmpfs Mounts in Apple Container

Use the --read-only flag to mount the container root as immutable and --tmpfs to add writable in-memory directories, with configurations flowing from CLI parsing in Flags.swift to runtime enforcement via ContainerConfiguration.

Apple Container provides mechanisms to harden container filesystems by preventing writes to the base image while allowing temporary data in memory. By configuring read-only root filesystems and tmpfs mounts, you can enhance security without sacrificing functionality for ephemeral data.

Understanding Read-Only Root Filesystems

The --read-only flag instructs the runtime to mount the container’s root filesystem as read-only, preventing any write operations on the base image layers.

How the --read-only Flag Works

The flag is defined in Sources/Services/ContainerAPIService/Client/Flags.swift at line 319. When the CLI parses the command, the value flows through Utility.swift at line 255, which copies management.readOnly into the runtime configuration. This ultimately sets the readOnly property in Sources/ContainerResource/Container/ContainerConfiguration.swift at line 53.

When the sandbox initializes, the runtime checks ContainerConfiguration.readOnly. If true, it passes the --read-only option to the underlying OCI runtime, mounting the rootfs with the ro flag.

Validation and Testing

The unit test TestCLIRunCommand.testRunCommandReadOnly in Tests/CLITests/Subcommands/Run/TestCLIRunCommand.swift at line 998 validates this behavior by attempting to create a file inside the container and verifying that the operation returns an error.

Implementing tmpfs Mounts

The --tmpfs flag adds an in-memory filesystem at a specified destination, creating a writable area that disappears when the container stops.

Parsing --tmpfs Arguments

The flag is defined in Sources/Services/ContainerAPIService/Client/Flags.swift at line 337. The Parser.tmpfsMounts method (lines 332-340 in Sources/Services/ContainerAPIService/Client/Parser.swift) processes these arguments, creating Filesystem objects via the Filesystem.tmpfs factory method found in Sources/ContainerResource/Container/Filesystem.swift at lines 28-34.

Filesystem Configuration and Options

The Filesystem.tmpfs factory sets type = .tmpfs, source = "tmpfs", and stores the destination path. These objects populate the ContainerConfiguration.mounts array. The parser handles size and mode options (lines 13-30 in Parser.swift), passing them to the kernel during mount as -o size=…,mode=… parameters.

Practical Configuration Examples

Running a Container with Read-Only Root

container run --read-only -it ubuntu:latest /bin/bash

The container’s root filesystem mounts as ro. Any attempt to modify files in / returns a permission error.

Adding a tmpfs Mount with Size Limits

container run \
    --tmpfs /run:size=64M,mode=1777 \
    -it ubuntu:latest /bin/bash

This creates a 64 MiB tmpfs at /run with permissions 1777. Data written here exists only in memory and vanishes when the container exits.

Combining Both Flags

container run \
    --read-only \
    --tmpfs /tmp:size=128M \
    alpine:latest /bin/sh -c "echo hello > /tmp/hi && cat /tmp/hi"

The base image remains immutable, while /tmp provides a writable, high-performance scratch space.

Programmatic Configuration in Swift

import ContainerizationOCI
import Container

// Build a configuration programmatically
var cfg = ContainerConfiguration(
    id: "demo",
    image: ImageDescription(name: "ubuntu:latest"),
    process: ProcessConfiguration(command: ["/bin/bash"])
)
cfg.readOnly = true               // read-only rootfs
cfg.mounts.append(.tmpfs(
    destination: "/run",
    options: ["size=64M", "mode=1777"]
))

// Pass `cfg` to the ContainerAPIService runtime

Summary

  • The --read-only flag creates an immutable root filesystem by setting ContainerConfiguration.readOnly to true, which the OCI runtime enforces with the ro mount flag.
  • The --tmpfs flag creates in-memory filesystems through the Parser.tmpfsMounts method, which generates Filesystem.tmpfs objects stored in ContainerConfiguration.mounts.
  • You can combine both flags to run containers with hardened, immutable base layers while maintaining writable temporary directories.
  • Configuration values flow from Flags.swiftUtility.swiftContainerConfiguration.swift before the runtime applies them to the sandbox.

Frequently Asked Questions

What does the --read-only flag do in Apple Container?

The --read-only flag mounts the container’s root filesystem as read-only, preventing any modifications to the base image layers. According to the source code in Sources/ContainerResource/Container/ContainerConfiguration.swift, this sets the readOnly boolean property, which the runtime translates to the OCI --read-only option during sandbox creation.

How do I specify size limits for tmpfs mounts?

Append the size option to the --tmpfs flag using the format --tmpfs /path:size=64M. The Parser.swift file (lines 13-30) handles these options, extracting the size and mode parameters and passing them to the kernel mount syscall. You can also specify mode (e.g., mode=1777) to control directory permissions.

Can I use both --read-only and --tmpfs together?

Yes. Combining --read-only with --tmpfs is a common security pattern that keeps the base image immutable while providing writable, ephemeral storage in memory. The ContainerConfiguration struct accommodates both the readOnly property and the mounts array simultaneously, as shown in the Swift example above.

How do I configure these options programmatically rather than via CLI?

Import the Container and ContainerizationOCI modules, create a ContainerConfiguration instance, set cfg.readOnly = true, and append .tmpfs(destination:options:) objects to the cfg.mounts array. This mirrors the CLI flag processing logic found in Utility.swift and Parser.swift, allowing direct integration with the ContainerAPIService.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →