# Specifying Custom Platforms for Action Runners in act Using `--platform`

> Learn to specify custom platforms for act runners using --platform. Override default Docker images and store mappings in .actrc for efficient GitHub Actions workflows.

- Repository: [nektos/act](https://github.com/nektos/act)
- Tags: how-to-guide
- Published: 2026-03-03

---

**Use `act -P <platform>=<image>` to override the default Docker image for any GitHub Actions runner label, storing persistent mappings in `.actrc` to avoid repeating flags on every run.**

The `nektos/act` tool executes GitHub Actions workflows locally by spinning up Docker containers that emulate GitHub-hosted runner environments. When you need to test workflows against custom container images or specific operating system versions, specifying custom platforms for action runners becomes essential. This guide explains how the `--platform` flag works under the hood, from CLI parsing in [`cmd/root.go`](https://github.com/nektos/act/blob/main/cmd/root.go) to image resolution in [`pkg/runner/run_context.go`](https://github.com/nektos/act/blob/main/pkg/runner/run_context.go).

## How the `--platform` Flag Works Internally

The `-P` flag accepts key-value pairs in the format `platform-name=docker-image`, allowing you to override act's built-in defaults through a multi-stage resolution process that merges user input with predefined mappings.

### Flag Registration and Input Collection

In [`cmd/root.go`](https://github.com/nektos/act/blob/main/cmd/root.go) (lines 82-84), the flag is registered as a string array that collects raw user input before any processing occurs:

```go
rootCmd.Flags().StringArrayVarP(&input.platforms, "platform", "P", []string{}, "custom image to use per platform")

```

These values populate the `platforms []string` field in the `Input` struct defined in [`cmd/input.go`](https://github.com/nektos/act/blob/main/cmd/input.go) (lines 22-23). This struct merely stores the raw slice for later use; no validation happens at this stage.

### Merging Defaults with User Overrides

The `newPlatforms()` function in [`cmd/platforms.go`](https://github.com/nektos/act/blob/main/cmd/platforms.go) (lines 7-21) initializes a map with four Ubuntu LTS defaults, then applies your overrides with case-insensitive key handling:

```go
func (i *Input) newPlatforms() map[string]string {
    platforms := map[string]string{
        "ubuntu-latest": "node:16-buster-slim",
        "ubuntu-22.04":  "node:16-bullseye-slim",
        "ubuntu-20.04":  "node:16-buster-slim",
        "ubuntu-18.04":  "node:16-buster-slim",
    }

    for _, p := range i.platforms {
        parts := strings.Split(p, "=")
        if len(parts) == 2 {
            platforms[strings.ToLower(parts[0])] = parts[1]
        }
    }
    return platforms
}

```

### Runtime Image Resolution

When the CLI creates the runner configuration in [`cmd/root.go`](https://github.com/nektos/act/blob/main/cmd/root.go) (lines 626-629), it passes the merged platform map via `Platforms: input.newPlatforms()`. During workflow execution, the `runsOnImage()` method in [`pkg/runner/run_context.go`](https://github.com/nektos/act/blob/main/pkg/runner/run_context.go) (lines 49-56) selects the appropriate container:

```go
func (rc *RunContext) runsOnImage(ctx context.Context) string {
    for _, platformName := range rc.runsOnPlatformNames(ctx) {
        image := rc.Config.Platforms[strings.ToLower(platformName)]
        if image != "" {
            return image
        }
    }
    return ""
}

```

If no custom image is found, the runner falls back to the built-in defaults defined in `newPlatforms()`. If even those are missing for the requested platform, the job is skipped with a warning about an unsupported platform (lines 17-24).

## Default Runner Images in act

By default, act maps common `runs-on` labels to lightweight Node.js images to minimize download times and disk usage. The `ubuntu-latest`, `ubuntu-20.04`, and `ubuntu-18.04` labels all resolve to `node:16-buster-slim`, while `ubuntu-22.04` uses `node:16-bullseye-slim`. These images provide a minimal environment sufficient for basic JavaScript and shell-based workflows, though they lack many tools present in actual GitHub-hosted runners.

## Specifying Custom Platforms via Command Line

Override defaults temporarily by passing one or more `-P` flags when invoking act:

```bash

# Use a custom image for a specific Ubuntu version

act -P ubuntu-20.04=myorg/custom-act-image:latest

# Override multiple platforms simultaneously

act \
  -P ubuntu-18.04=myorg/act-ubuntu18:latest \
  -P ubuntu-22.04=myorg/act-ubuntu22:latest

```

## Persistent Configuration with `.actrc`

Store platform mappings in a configuration file to avoid repeating flags on every invocation. Act searches for `.actrc` in three locations (in order of precedence): `./.actrc`, `~/.actrc`, and `~/.config/act/actrc`.

Add your custom platform lines to the file:

```text
-P ubuntu-20.04=myorg/custom-act-image:latest
-P ubuntu-22.04=myorg/act-ubuntu22:latest

```

On first run, if no configuration file exists, act executes the `defaultImageSurvey` function in [`cmd/root.go`](https://github.com/nektos/act/blob/main/cmd/root.go) to interactively prompt for preferred images, then writes the resulting `-P` lines to the first available config file location. This survey only appears once per machine.

## Summary

- **The `--platform` flag** (`-P`) accepts `platform-name=docker-image` pairs to override act's built-in defaults for any runner label.
- **Internal resolution** flows from [`cmd/root.go`](https://github.com/nektos/act/blob/main/cmd/root.go) (flag parsing) → [`cmd/platforms.go`](https://github.com/nektos/act/blob/main/cmd/platforms.go) (map merging with lowercase normalization) → [`pkg/runner/run_context.go`](https://github.com/nektos/act/blob/main/pkg/runner/run_context.go) (runtime lookup via `runsOnImage()`).
- **Default images** map Ubuntu LTS releases to lightweight Node.js containers (`node:16-buster-slim` or `node:16-bullseye-slim`).
- **Persistent configuration** is stored in `.actrc` files, automatically loaded from the current directory or home directory.
- **Fallback behavior** uses built-in defaults if no custom mapping matches, or skips the job if the platform is entirely unrecognized.

## Frequently Asked Questions

### Can I use `--platform` to run macOS or Windows containers?

No. The `--platform` flag only affects Linux-based Docker images. Act cannot execute macOS or Windows containers because Docker requires a Linux host kernel. While you can map `macos-latest` to a Linux image (e.g., `-P macos-latest=ubuntu:22.04`), this executes a Linux container that merely pretends to be macOS for workflow parsing purposes, not actual macOS.

### Why does act use Node.js images as defaults instead of full GitHub-hosted runner images?

The default `node:16-buster-slim` images prioritize fast download speeds and minimal disk usage for local development. Full GitHub-hosted runner images exceed 10GB and contain hundreds of pre-installed tools that most local workflows do not require. You can override these with full images using `-P ubuntu-latest=nektos/act-environments-ubuntu:18.04` when your workflow depends on specific pre-installed software.

### How does act handle case sensitivity in platform names?

Platform names are normalized to lowercase before lookup. In [`cmd/platforms.go`](https://github.com/nektos/act/blob/main/cmd/platforms.go), the `newPlatforms()` function converts both the default keys and user-supplied keys to lowercase using `strings.ToLower()`. Similarly, `runsOnImage()` in [`pkg/runner/run_context.go`](https://github.com/nektos/act/blob/main/pkg/runner/run_context.go) lowercases the workflow's `runs-on` value before checking against the configuration map, ensuring `Ubuntu-20.04` and `ubuntu-20.04` resolve identically.

### What happens if I specify a platform mapping that isn't used in my workflow?

Act ignores unused platform mappings without error. The platform map is built globally at startup in `newPlatforms()`, but `runsOnImage()` only queries the map when processing a job's actual `runs-on` field. Unused mappings consume negligible memory and do not affect execution speed or container startup time.