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-onlyflag creates an immutable root filesystem by settingContainerConfiguration.readOnlytotrue, which the OCI runtime enforces with theromount flag. - The
--tmpfsflag creates in-memory filesystems through theParser.tmpfsMountsmethod, which generatesFilesystem.tmpfsobjects stored inContainerConfiguration.mounts. - You can combine both flags to run containers with hardened, immutable base layers while maintaining writable temporary directories.
- Configuration values flow from
Flags.swift→Utility.swift→ContainerConfiguration.swiftbefore 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →