- Job-level environment variables (scoped to a single job)
- Workflow-level environment variables (shared across jobs)
- Step-level environment variables (scoped to a specific step)
- Common runtime errors caused by missing env vars
- Best practices for secrets and CI databases
Scopes and precedence
GitHub Actions supportsenv at three levels:
- Workflow-level: declared at the top level, available to every job and step unless overridden.
- Job-level: declared inside a job; available only to steps in that job.
- Step-level: declared inside a step; overrides job/workflow variables with the same name.
Step-level
env entries override job-level and workflow-level variables with the same name. Use step-level for single-step overrides and job-level for values shared by multiple steps in the same job.Job-level environment variables
Define job-level variables underjobs.<job_id>.env. These values are available to all steps in that job only.
Example: a job-level env for tests and build steps.
job1.env, they are not visible to other jobs (e.g., job2) unless you duplicate or lift them to workflow-level.
Why missing env vars cause runtime errors
If your application reads required configuration from environment variables (for example, a PydanticSettings class), missing values can cause immediate initialization failures. Typical CI error output from Pydantic looks like this:
Step-level environment variables
Useenv inside a specific step to provide values only that step needs. Step-level vars override those at job or workflow scope when names collide.
Example: job-level env with a conditional step that uses step-level overrides. Note the GitHub Actions expression is shown in the YAML:
Built-in environment variables
Runners expose many built-in environment variables such asGITHUB_REPOSITORY, GITHUB_RUN_NUMBER, and GITHUB_SHA. Use these when you need metadata about the current workflow run. See the GitHub Docs for the full list:

Workflow-level environment variables (shared across jobs)
If many jobs require the same variables, define them at the workflow root (one level abovejobs). These variables propagate to all jobs and steps unless overridden.
Example:
Common workflow mistakes
- Leaving a job with no
stepswill cause the workflow parser to reject the file. Example error:
- Expecting
job1.envto be available injob2—job-level variables are not shared.
Runner logs and environment validation
When a runner executes a step, it often prints an environment dump that shows the env variables available to that step. This is helpful for debugging and confirming that variables are set. For example:Note about testing databases in CI
If your tests rely on a database atlocalhost, you must either start a database service on the runner or connect to an external test database. GitHub Actions supports service containers (for example, the official postgres service) to spin up databases during the job:
- Use the GitHub Actions service containers feature: https://docs.github.com/en/actions/using-jobs/using-service-containers/about-service-containers
Best practices for secrets
Never hard-code sensitive values (production credentials, private keys, tokens) in your repository or workflow YAML. Instead, store them in GitHub Secrets and reference them via thesecrets context.
Example:
Never store sensitive credentials in plaintext in your repository. Use GitHub Secrets (or another secrets manager) and reference them via the
secrets context so the values are kept private and not visible in your repo.Quick reference table
Summary
- Use workflow-level
envfor values shared across jobs. - Use job-level
envfor values used by multiple steps within the same job. - Use step-level
envfor single-step values and to override broader scopes. - Always use
secretsfor sensitive data and never commit credentials to the repo. - Ensure CI services (like databases) are available to the runner when tests expect them.
Links and references
- GitHub Actions: Environment variables
- GitHub Actions: Encrypted secrets
- Using service containers in GitHub Actions
- Pydantic settings management