# Using `act --var` and `--var-file` to Pass Workflow Variables in Local GitHub Actions

> Learn to pass workflow variables to local GitHub Actions using act flags --var and --var-file. Access them seamlessly with ${{ vars.KEY }} syntax.

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

---

**The `act` CLI accepts workflow variables via `--var KEY=VALUE` flags or `--var-file path` files, merging them into a Vars map that workflows access via `${{ vars.KEY }}` syntax.**

Running GitHub Actions locally with [nektos/act](https://github.com/nektos/act) often requires injecting dynamic values without hardcoding them into workflow YAML. The `--var` and `--var-file` flags solve this by piping custom variables into the runner's expression engine, letting you test workflows against different environments or secrets managers without committing sensitive data.

## How `act` Processes Variable Flags Internally

The implementation spans the command layer and the runner engine. Here is the exact path your variables take from CLI argument to workflow expression.

### Flag Registration and Input Handling

The root command defines the interface in [`cmd/root.go`](https://github.com/nektos/act/blob/main/cmd/root.go). Line 79 registers `--var` as a string array, while line 109 registers `--var-file` as a single string path.

```go
// cmd/root.go:79
rootCmd.PersistentFlags().StringArrayVarP(&input.vars, "var", "", []string{}, "")

// cmd/root.go:109
rootCmd.PersistentFlags().StringVarP(&input.varfile, "var-file", "", "", "")

```

These values populate the `Input` struct defined in `cmd/input.go:18-20`, which holds the raw slices before validation.

```go
type Input struct {
    vars     []string
    varfile  string
    // ...
}

```

When the CLI resolves the file path, the `Varfile()` method (lines 94-96) interprets relative paths against the working directory, ensuring the subsequent loader finds the file regardless of where you invoke `act`.

### Parsing and Loading Variable Definitions

`act` reuses existing environment-parsing machinery for variable files. The `readEnvs` function (called at `cmd/root.go:445-447`) ingests the file specified by `--var-file`, supporting both `.env` style (`KEY=VALUE`) and YAML formats, and returns a `map[string]string`.

Individual `--var` arguments are processed by `newSecrets` in `cmd/secrets.go:14-36`. Despite the name, this helper parses both secrets and variables. It splits each `NAME[=VALUE]` entry, upper-cases the key, and—if the value is omitted—reads from the host environment or prompts interactively.

### Merging CLI and File Variables

Precedence is determined by load order. First, `readEnvs` loads the file content into a map. Then, `newSecrets` iterates over the `--var` slice and writes into the same map. Because the CLI processing happens second, **command-line variables override file definitions** when keys collide.

### Runtime Exposure via the Vars Map

Once merged, the map is stored in `runner.Config.Vars`. The `Config` struct definition in `pkg/runner/runner.go:38-41` includes:

```go
type Config struct {
    Vars map[string]string
    // ...
}

```

During workflow execution, the expression evaluator calls `getWorkflowVars` (located at `pkg/runner/expression.go:92-94`), which returns `rc.Config.Vars`. This makes every entry available to the workflow expression engine as `${{ vars.NAME }}`.

## Practical Examples for Local Testing

### Pass a Single Variable Inline

Supply a build flag without touching the workflow file:

```bash
act -j build --var BUILD_TYPE=debug

```

Inside the workflow, reference it via the expression context:

```yaml
- run: echo "Building ${{ vars.BUILD_TYPE }} image"

```

### Load Multiple Variables from a File

Create `staging.vars`:

```ini
API_ENDPOINT=https://staging.example.com
TIMEOUT_SECONDS=30
DEBUG_MODE=true

```

Execute the job with the file path:

```bash
act -j deploy --var-file staging.vars

```

The runner loads all three entries into the Vars map, making `${{ vars.API_ENDPOINT }}` and siblings available immediately.

### Override File Values with CLI Flags

When you need to tweak one value without editing the file, combine both flags. The CLI value wins:

```bash

# staging.vars contains LOG_LEVEL=info

act -j test --var-file staging.vars --var LOG_LEVEL=debug

```

`LOG_LEVEL` resolves to `debug` because the `--var` assignment overwrites the file entry during the merge step.

### Access Variables in Workflow Expressions and Shell

Variables appear in both the expression context and the shell environment:

```yaml
jobs:
  example:
    runs-on: ubuntu-latest
    steps:
      - name: Via expression syntax
        run: echo "Endpoint is ${{ vars.API_ENDPOINT }}"
      - name: Via shell environment
        run: echo "Timeout is $TIMEOUT_SECONDS"

```

Both methods work because the runner injects the Vars map into the step's environment variables in addition to the expression evaluator.

## Summary

- **`--var`** accepts individual `KEY=VALUE` pairs and can be repeated; missing values fall back to host environment variables or interactive prompts.
- **`--var-file`** points to a `.env` or YAML file containing multiple definitions, resolved relative to the working directory.
- **Precedence**: CLI flags override file entries when names conflict, as implemented by sequential map writes in [`cmd/root.go`](https://github.com/nektos/act/blob/main/cmd/root.go).
- **Access**: Variables surface in workflows as `${{ vars.NAME }}` via `getWorkflowVars` in [`pkg/runner/expression.go`](https://github.com/nektos/act/blob/main/pkg/runner/expression.go), and also propagate to shell environments for `$NAME` access.

## Frequently Asked Questions

### Does `act --var` support reading values from the host environment?

Yes. If you omit the value in a `--var` argument (e.g., `--var MY_TOKEN`), the `newSecrets` parser in `cmd/secrets.go:14-36` looks for `MY_TOKEN` in the host environment. If it is not found and stdin is a TTY, `act` prompts you interactively to enter the value.

### Can I use YAML format instead of `.env` style in var files?

Yes. The `readEnvs` function (used at `cmd/root.go:445-447`) accepts both formats. You can use traditional `KEY=VALUE` lines or a YAML mapping structure; the parser normalizes either into the internal `map[string]string` required by the runner configuration.

### What happens if the same variable is defined in both `--var-file` and `--var`?

The `--var` value takes precedence. The loading sequence in [`cmd/root.go`](https://github.com/nektos/act/blob/main/cmd/root.go) first populates the map from the file via `readEnvs`, then processes the CLI slice via `newSecrets`. Because the latter writes after the former, duplicate keys are overwritten by the command-line specification.

### How do I list or debug which variables are actually loaded?

`act` does not print the Vars map by default. To verify injection, add a step that dumps the context:

```yaml
- run: echo '${{ toJson(vars) }}'

```

This outputs the entire Vars object as JSON during the run, letting you confirm that `act --var` and `--var-file` merged correctly before your production logic executes.