How to Run a Detached Nginx Container with Port Publishing

Use container run -d -p 8080:80 nginx:latest to start an Nginx container in the background with host port 8080 mapped to container port 80.

The apple/container repository provides a Swift-based container runtime that supports standard Docker-like workflows. Running a detached Nginx container with port publishing requires understanding how the container run command parses the -d and -p flags and delegates to the runtime service.

How the container run Command Works

The container run command serves as the primary interface for creating and starting containers in the apple/container project. When you execute this command with the -d (or --detach) flag, the CLI implementation in Sources/ContainerCommands/Container/ContainerRun.swift processes the request through several flag categories defined in the ContainerRun struct (lines 38‑55).

The execution flow follows these steps:

  1. Flag Parsing: The command processes processFlags, resourceFlags, and managementFlags, extracting the detach boolean and publish port mappings.

  2. Container Creation: The CLI builds a ContainerCreateOptions object (lines 11‑13), incorporating settings like autoRemove based on the --rm flag.

  3. Runtime Registration: The client.create() method registers the container with the runtime, passing the image name and configuration including port specifications.

  4. Detach Logic: When managementFlags.detach is true, the code enters a conditional block (lines 54‑60) that calls process.start(), closes I/O handling, prints the generated container ID, and returns immediately to the shell.

  5. Network Setup: The actual port binding occurs in Sources/Services/RuntimeLinux/Server/RuntimeService.swift, which creates the network namespace, attaches virtual NICs, and binds host sockets according to the publish specifications.

Port Publishing Syntax and Configuration

The -p (or --publish) flag uses the standard container port mapping syntax:


[host-ip:]host-port:container-port[/protocol]

For Nginx, which listens on port 80 internally, you typically map this to an available host port (e.g., 8080). The runtime handles this by creating a listening socket on the host that forwards traffic to the container's network namespace.

Practical Code Examples

1. Minimal Detached Command

Run Nginx in the background with a simple port mapping:

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

This creates a container with a random ID, starts it detached, and publishes host port 8080 to container port 80. The command prints the container ID to stdout and exits immediately.

2. Named Container with CID File

For easier management, assign a custom name and write the container ID to a file:

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

The --name web parameter allows you to reference the container as web in subsequent commands. The --cidfile option (handled in ContainerRun.swift lines 38‑52) persists the generated ID to /tmp/web.cid for automation scripts.

3. Multiple Ports and Environment Variables

Expose both HTTP and HTTPS ports while passing configuration via environment variables:

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

This maps port 8080 to 80 and 8443 to 443. The -e flag injects environment variables through processFlags, processed by Utility.containerConfigFromFlags (lines 95‑104 in ContainerRun.swift).

Verifying Container Status and Port Bindings

Confirm your detached container is running and inspect its network configuration:


# List running containers

container list

# Inspect detailed port configuration

container inspect web

The inspect output includes a Ports array showing the active bindings:

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

Access the Nginx welcome page by navigating to http://localhost:8080 in your browser.

Summary

  • Detached mode (-d) returns control to the terminal immediately while the container runs in the background, implemented in ContainerRun.swift lines 54‑60.
  • Port publishing (-p host:container) creates host-side listeners that forward to the container's network namespace, configured during runtime initialization in RuntimeService.swift.
  • Container naming (--name) and CID files (--cidfile) simplify container management and automation.
  • Verify active port mappings using container inspect to confirm the runtime correctly bound your specified ports.

Frequently Asked Questions

What is the difference between -d and --detach?

There is no functional difference; -d is the shorthand alias for --detach. Both flags set managementFlags.detach to true in ContainerRun.swift, triggering the code path that starts the container process without attaching the current terminal's standard input, output, and error streams.

Can I publish a range of ports when running a detached container?

Yes, you can specify multiple -p flags to map several ports or port ranges. Each flag is parsed and added to the container's network configuration. The runtime (RuntimeService.swift) iterates through these specifications to create the corresponding host listening sockets and firewall rules for traffic forwarding.

How does the runtime handle port conflicts?

If the specified host port is already in use, the client.create() call in ContainerRun.swift will fail during the runtime service initialization phase. The error propagates back to the CLI, which prints a binding error message without starting the container. You must specify an available host port or omit the host port to let the runtime assign an ephemeral port automatically.

Where are the container logs stored when running detached?

When running in detached mode, the container process continues writing to log files managed by the runtime service rather than the parent terminal. You can access these logs using the container logs command followed by the container name or ID. The logging driver and destination are configured through the processFlags and resource management options passed during container creation.

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 →