# Setting Environment Variables in act Workflows: A Complete Guide

> Master setting environment variables in act workflows. Learn to use CLI flags, env files, and workflow job or step definitions for seamless variable management with nektos/act.

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

---

**You can set environment variables in act using CLI flags (`-e`), `.env` files, workflow-level `env:` blocks, job-level definitions, and step-level overrides, with the final map being constructed through a hierarchical merge process implemented in the Go source code.**

The `nektos/act` repository provides a local GitHub Actions runner that must precisely emulate cloud behavior, making **setting environment variables in act workflows** a multi-layered process. The implementation spans CLI parsing, YAML workflow interpretation, and Docker container orchestration to ensure variable resolution matches GitHub's production environment.

## How act Constructs the Runtime Environment

Act builds the execution environment through five distinct stages, each handled by specific components in the codebase. This architecture ensures that variables defined at different scopes (global, job, step) merge correctly before reaching your containers.

### Stage 1: CLI and File Parsing

When you execute `act run`, the command layer immediately begins collecting environment data. In [`cmd/root.go`](https://github.com/nektos/act/blob/main/cmd/root.go), the `parseEnvs` function (lines 20‑30) processes `-e` flags from the command line, while `readEnvsEx` (lines 49‑68) loads variables from the default `.env` file or a custom file specified via `--env-file`. These values populate an initial map that serves as the foundation for all subsequent merging.

### Stage 2: Workflow and Job-Level Merging

The parsed workflow definition undergoes environment resolution through the `environment` helper in [`pkg/model/workflow.go`](https://github.com/nektos/act/blob/main/pkg/model/workflow.go) (lines 72‑79), which converts YAML `env:` blocks into Go maps. Later, [`pkg/runner/run_context.go`](https://github.com/nektos/act/blob/main/pkg/runner/run_context.go) implements the `GetEnv` method (lines 77‑88) to perform the first major merge: it combines the global workflow environment, job-specific definitions, and the CLI-provided map from stage one. This method also injects the marker variable `ACT=true` to identify the local runtime.

### Stage 3: Step-Level Preparation and Container Injection

Before any step executes, `setupEnv()` in [`pkg/runner/step.go`](https://github.com/nektos/act/blob/main/pkg/runner/step.go) (lines 245‑267) performs the final merge, overlaying step-specific `env:` entries onto the job-level map and expanding `${{ }}` expressions. For Docker-based steps, [`pkg/container/docker_cli.go`](https://github.com/nektos/act/blob/main/pkg/container/docker_cli.go) (line 186) translates the completed environment map into `-e KEY=VALUE` flags passed to the Docker CLI via `container.NewContainerInput`.

## CLI and File-Based Environment Configuration

The simplest method for **setting environment variables in act workflows** involves command-line flags and local files. Act automatically looks for a `.env` file in your repository root unless you specify an alternative.

Create a standard `.env` file with key-value pairs:

```text

# .env in the repository root

DATABASE_URL=postgres://user:pass@localhost/db
DEBUG=true

```

Load a custom file or add individual variables at runtime:

```bash
act -e FOO=bar --env-file .custom.env

```

The `-e` flag invokes `parseEnvs` for single variables, while `--env-file` triggers `readEnvs` to parse the specified path, as implemented in [`cmd/root.go`](https://github.com/nektos/act/blob/main/cmd/root.go).

## Workflow and Job-Level Environment Definitions

GitHub Actions workflows support `env:` blocks at both the workflow and job levels, with act processing these through [`pkg/model/workflow.go`](https://github.com/nektos/act/blob/main/pkg/model/workflow.go). Job-level definitions override workflow-level values for that specific job.

Define global and job-scoped variables in your workflow YAML:

```yaml
name: Build
on: [push]

env:
  GLOBAL_VAR: "global"
  DEBUG: "false"

jobs:
  build:
    runs-on: ubuntu-latest
    env:               # job-level env overrides/extends the workflow-wide block

      JOB_VAR: "job"
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Echo vars
        run: |
          echo "GLOBAL_VAR=$GLOBAL_VAR"
          echo "JOB_VAR=$JOB_VAR"
          echo "DEBUG=$DEBUG"
          echo "DATABASE_URL=$DATABASE_URL"

```

The `RunContext.GetEnv()` method merges these layers according to GitHub's precedence rules, ensuring job variables take priority over workflow globals while preserving CLI-injected values.

## Step-Level Overrides and Special Variables

Individual steps can define their own environment maps that override all higher-level definitions. This mechanism also handles the special `GITHUB_ENV` and `GITHUB_OUTPUT` files that GitHub Actions uses for persistence between steps.

Override variables for specific steps:

```yaml
steps:
  - name: Override DEBUG for this step
    env:
      DEBUG: "true"
    run: echo "DEBUG is $DEBUG"
  - name: Set persistent env
    run: echo "MY_VAR=value" >> $GITHUB_ENV

```

The `setupEnv()` function in [`pkg/runner/step.go`](https://github.com/nektos/act/blob/main/pkg/runner/step.go) executes this merge, evaluates expressions, and configures the special GitHub-specific files before your step code runs.

## Programmatic Environment Configuration in Go

If embedding act into your own tooling, you can construct environment maps programmatically using the same internal functions that power the CLI:

```go
// Example snippet from a Go program using act's internals
env := map[string]string{
    "FOO": "bar",
}
// Load a .env file
act.ReadEnvs("./my.env", env) // see cmd/root.go readEnvsEx

// Create a RunContext (simplified)
rc := &act.RunContext{
    Config: &act.Config{Env: env},
}
finalEnv := rc.GetEnv() // merges workflow + job envs
fmt.Println(finalEnv["FOO"]) // prints "bar"

```

This approach leverages `readEnvsEx` from [`cmd/root.go`](https://github.com/nektos/act/blob/main/cmd/root.go) and `GetEnv` from [`pkg/runner/run_context.go`](https://github.com/nektos/act/blob/main/pkg/runner/run_context.go) to ensure consistency with standard act behavior.

## Summary

- **CLI flags** (`-e`) and **`.env` files** provide the base environment through [`cmd/root.go`](https://github.com/nektos/act/blob/main/cmd/root.go) functions `parseEnvs` and `readEnvsEx`.
- **Workflow-level** and **job-level** `env:` blocks merge in `RunContext.GetEnv()` within [`pkg/runner/run_context.go`](https://github.com/nektos/act/blob/main/pkg/runner/run_context.go), following GitHub's precedence hierarchy.
- **Step-level** definitions override higher scopes via `setupEnv()` in [`pkg/runner/step.go`](https://github.com/nektos/act/blob/main/pkg/runner/step.go), which also handles expression expansion and special files like `GITHUB_ENV`.
- **Docker containers** receive the final environment map as `-e` flags through `container.NewContainerInput` in [`pkg/container/docker_cli.go`](https://github.com/nektos/act/blob/main/pkg/container/docker_cli.go).
- The marker variable `ACT=true` is automatically injected to identify local execution contexts.

## Frequently Asked Questions

### How do I pass environment variables to act from the command line?

Use the `-e` flag followed by `KEY=VALUE` pairs when invoking act. Multiple variables require multiple `-e` flags: `act -e VAR1=val1 -e VAR2=val2`. These values are parsed by the `parseEnvs` function in [`cmd/root.go`](https://github.com/nektos/act/blob/main/cmd/root.go) and become part of the base environment before workflow parsing begins.

### Can I use a custom .env file with act instead of the default one?

Yes, specify an alternative file path using the `--env-file` flag: `act --env-file ./config/production.env`. The `readEnvsEx` function in [`cmd/root.go`](https://github.com/nektos/act/blob/main/cmd/root.go) handles this file path and populates the environment map before workflow execution starts.

### How does act handle environment variable precedence when the same key is defined in multiple places?

Act follows GitHub Actions' standard precedence rules: step-level definitions override job-level, which override workflow-level, which override CLI/file-provided values. The `GetEnv()` method in [`pkg/runner/run_context.go`](https://github.com/nektos/act/blob/main/pkg/runner/run_context.go) performs the workflow and job merging, while `setupEnv()` in [`pkg/runner/step.go`](https://github.com/nektos/act/blob/main/pkg/runner/step.go) applies step-specific overrides as the final layer.

### Can I set environment variables dynamically during workflow execution?

Yes, write to the `GITHUB_ENV` file within any step: `echo "KEY=value" >> $GITHUB_ENV`. The `setupEnv()` function in [`pkg/runner/step.go`](https://github.com/nektos/act/blob/main/pkg/runner/step.go) processes these files between steps, making the variables available to subsequent steps in the same job. This mirrors GitHub Actions' behavior for persistent environment modifications.