How to Run a Detached Nginx Container with Port Publishing in Apple Container

Use container run -d -p 8080:80 nginx:latest to start a background Nginx container and map host port 8080 to container port 80.

The apple/container repository provides a Swift-based container runtime that supports standard Docker-like workflows. Learning how to run a detached Nginx container with port publishing allows you to deploy web services that persist after terminal disconnect and remain accessible via host networking.

Understanding the Container Run Command

The container run command serves as the primary interface for creating and starting containers in the Apple Container ecosystem. According to the source code in ContainerRun.swift, this command orchestrates the entire lifecycle from flag parsing to process initialization.

How Detached Mode Works

The -d or --detach flag triggers background execution. In Sources/ContainerCommands/Container/ContainerRun.swift (lines 54-60), the implementation checks managementFlags.detach and, when true, executes process.start(), immediately closes I/O handling, prints the generated container ID, and returns control to the terminal. This leaves the container process running within the runtime sandbox while freeing your shell.

Port Publishing Syntax

Port publishing uses the -p or --publish flag with the syntax [host‑ip:]host‑port:container‑port[/protocol]. This maps traffic from the host's network interface to the container's internal ports. For Nginx, which listens on port 80 by default, you typically expose it on a host port like 8080.

Step-by-Step Implementation Flow

When you execute container run -d -p 8080:80 nginx:latest, the following occurs:

  1. Flag Parsing: The CLI parses processFlags, resourceFlags, and managementFlags (lines 38-55 in ContainerRun.swift), extracting detach, publish, and optional name parameters.

  2. Configuration Building: The system constructs a ContainerCreateOptions object. The autoRemove property is set based on the --rm flag (lines 11-13 in ContainerRun.swift).

  3. Container Registration: The client.create() method registers the container with the runtime, passing the image name (nginx:latest) and encoded port specifications.

  4. Background Launch: When detach is true, the code enters the if detach { … } block (lines 54-60), starting the process without waiting for completion.

  5. Network Setup: The runtime service (RuntimeService.swift) creates the network namespace, virtual NIC, and host-side listening socket that forwards traffic to the container's port 80.

Practical Code Examples

Minimal Detached Command

Start Nginx in the background with basic port mapping:

container run -d -p 8080:80 nginx:latest

This creates a randomly named container, runs it detached, and publishes host port 8080 to container port 80. The command returns the container ID immediately.

Named Container with CID File

For easier management and script integration:

container run -d --name web \
  -p 8080:80 \
  --cidfile /tmp/web.cid \
  nginx:latest

The --name web parameter enables referral via container inspect web, while --cidfile /tmp/web.cid writes the container ID to disk (handled in ContainerRun.swift lines 38-52).

Multiple Ports and Environment Variables

Expose both HTTP and HTTPS while passing configuration:

container run -d \
  --name web \
  -p 8080:80 -p 8443:443 \
  -e NGINX_HOST=example.com \
  nginx:latest

Environment variables are injected via processFlags through Utility.containerConfigFromFlags (lines 95-104 in ContainerRun.swift).

Verifying the Container

Check running containers and port bindings:

container list
container inspect web

The inspect output includes a Ports section showing:

"Ports": [
  {
    "HostIP": "0.0.0.0",
    "HostPort": "8080",
    "ContainerPort": "80",
    "Protocol": "tcp"
  }
]

Key Source Files

Understanding the implementation requires examining these files:

Summary

  • Use container run -d -p 8080:80 nginx:latest to run a detached Nginx container with port publishing.
  • The -d flag executes process.start() and returns immediately while the runtime manages the background process.
  • Port publishing syntax follows [host-ip:]host-port:container-port[/protocol] and is processed by the runtime's network namespace setup.
  • Source implementation resides primarily in ContainerRun.swift (CLI handling) and RuntimeService.swift (network forwarding).
  • Name containers with --name and track IDs with --cidfile for operational convenience.

Frequently Asked Questions

What does the -d flag do in container run?

The -d or --detach flag tells the CLI to start the container and return immediately without waiting for the process to exit. According to ContainerRun.swift (lines 54-60), this executes process.start(), closes I/O streams, prints the container ID, and exits the CLI process while the container continues running in the runtime sandbox.

How do I publish multiple ports when running a detached container?

Specify multiple -p flags when invoking container run. For example, -p 8080:80 -p 8443:443 maps both HTTP and HTTPS ports. Each flag follows the [host-ip:]host-port:container-port[/protocol] syntax and is processed as part of the managementFlags.publish array in the Swift implementation.

Where is the container ID stored when using --cidfile?

The container ID is written to the file path specified by the --cidfile flag immediately after container creation. In ContainerRun.swift (lines 38-52), the implementation generates the container ID during the creation phase and persists it to the specified file before the detach logic executes.

How does the runtime handle port forwarding?

The actual networking implementation resides in RuntimeService.swift rather than the CLI. The runtime creates a virtual network interface for the container, attaches it to the user-defined bridge, and binds the host socket according to the publish specifications. This Linux sandbox setup forwards traffic from the host port to the container's internal port 80.

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 →