Handling tmpfs Mounts and Read-Only Root Filesystems in Apple Container
Apple Container hardens container security by supporting read-only root filesystems via the --read-only flag and ephemeral tmpfs mounts via --tmpfs, implemented through the ContainerConfiguration struct and parsed in Flags.swift and Parser.swift.
The apple/container repository provides robust mechanisms for handling tmpfs mounts and read-only root filesystems in containers, allowing developers to secure workloads by preventing write access to base image layers while providing writable in-memory spaces for temporary data.
Read-Only Root Filesystems
CLI Flag and Configuration
The --read-only flag is defined in Sources/Services/ContainerAPIService/Client/Flags.swift at line 319. When users invoke this flag, the CLI parser sets management.readOnly to true, which signals the runtime to mount the container's root filesystem as read-only.
Implementation Pipeline
In Sources/Services/ContainerAPIService/Client/Utility.swift at line 255, the boolean value is copied into config.readOnly from the management configuration. This property is defined in Sources/ContainerResource/Container/ContainerConfiguration.swift at line 53. When the sandbox initializes, the runtime checks ContainerConfiguration.readOnly, and if true, mounts the rootfs with the ro flag, effectively passing --read-only to the underlying OCI runtime.
Validation
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 expecting a permission error.
Tmpfs Mounts
Parsing the --tmpfs Flag
The --tmpfs flag is defined in Sources/Services/ContainerAPIService/Client/Flags.swift at line 337, accepting arguments in the format --tmpfs /run:size=64M,mode=1777. The parser method tmpfsMounts in Sources/Services/ContainerAPIService/Client/Parser.swift (lines 332-340) processes these arguments, validates the mount specifications, and returns a deduplicated array of tmpfs configurations.
Filesystem Object Construction
Each tmpfs mount is converted to a Filesystem.tmpfs object via the factory method in Sources/ContainerResource/Container/Filesystem.swift at lines 28-34. This implementation sets type = .tmpfs, source = "tmpfs", and stores the destination path along with any provided options in the ContainerConfiguration.mounts array.
Mount Options
The Parser.mount method handles size and mode directives (around lines 13-30 in Parser.swift), translating these into -o size=…,mode=… flags when the runtime mounts the filesystem during sandbox startup.
Practical Examples
Running with a Read-Only Rootfs
container run --read-only -it ubuntu:latest /bin/bash
The container's root filesystem mounts as ro. Any attempt to modify files in / will fail, as validated by the test suite.
Adding a Tmpfs Mount
# Mount a 64 MiB tmpfs at /run with mode 1777
container run \
--tmpfs /run:size=64M,mode=1777 \
-it ubuntu:latest /bin/bash
Data written to /run persists only in memory and disappears when the container stops.
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 in-memory area.
Programmatic Configuration
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 runtime (ContainerAPIService)
Summary
- Read-only rootfs: Set via
--read-onlyinFlags.swift, stored inContainerConfiguration.readOnly, and enforced by the OCI runtime with theromount flag. - Tmpfs mounts: Created via
--tmpfsinFlags.swift, parsed byParser.tmpfsMounts, and constructed asFilesystem.tmpfsobjects inFilesystem.swift. - Validation: The
TestCLIRunCommand.testRunCommandReadOnlytest verifies that write operations fail on read-only root filesystems. - Programmatic access: Both features can be configured directly through
ContainerConfigurationin Swift code.
Frequently Asked Questions
What is a read-only root filesystem in Apple Container?
A read-only root filesystem is a security feature that mounts the container's base image layers as read-only, preventing any modifications to the root filesystem. According to the source code in Sources/ContainerResource/Container/ContainerConfiguration.swift, this is controlled by the readOnly boolean property, which when set to true, instructs the OCI runtime to apply the ro mount flag to the rootfs.
How do tmpfs mounts work in Apple Container?
Tmpfs mounts create ephemeral, in-memory filesystems that exist only for the duration of the container's lifecycle. As implemented in Sources/ContainerResource/Container/Filesystem.swift, the Filesystem.tmpfs factory creates mount objects with type = .tmpfs and source = "tmpfs", allowing the runtime to mount memory-based storage at specified destinations like /tmp or /run.
Can I combine read-only rootfs with tmpfs mounts?
Yes, combining --read-only with --tmpfs flags provides a hardened security posture where the base image is immutable but specific paths remain writable. This pattern is common for running stateless applications where you need temporary writable space for caches or runtime data while ensuring the application binaries cannot be modified.
What are the performance implications of using tmpfs?
Tmpfs mounts provide fast, in-memory I/O since they avoid disk writes, but they consume RAM from the host system. The size option (parsed in Parser.swift) limits memory consumption, while the mode option sets Unix permissions. Data stored in tmpfs is lost when the container stops, making it suitable for temporary data only.
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 →