Letโs talk about environment variables in GitHub Actions โ those little gremlins that either make your CI/CD run silky smooth or throw a wrench in your perfectly crafted YAML.
If you’ve ever squinted at your pipeline and wondered, โWhere the heck should I declare this ANSIBLE_CONFIG thing so it doesnโt vanish into the void between steps?โ, youโre not alone. Iโve been there. Iโve screamed at $GITHUB_ENV. Iโve misused export. Iโve over-engineered echo. But fear not, dear reader โ Iโve distilled it down so you donโt have to.
In this post, weโll look at the right ways (and a few less right ways) to set environment variables โ and more importantly, when to use static vs dynamic approaches.
๐ง Static Variables: Set It and Forget It
Got a variable like ANSIBLE_STDOUT_CALLBACK=yaml thatโs the same every time? Congratulations, youโve got yourself a static variable! These are the boring, predictable, low-maintenance types that make your CI life a dream.
โ
Best Practice: Job-Level env
If your variable is static and used across multiple steps, this is the cleanest, classiest, and least shouty way to do it:
jobs:
my-job:
runs-on: ubuntu-latest
env:
ANSIBLE_CONFIG: ansible.cfg
ANSIBLE_STDOUT_CALLBACK: yaml
steps:
- name: Use env vars
run: echo "ANSIBLE_CONFIG is $ANSIBLE_CONFIG"
Why it rocks:
- ๐ Super readable
- ๐ฆ Available in every step of the job
- ๐งผ Keeps your YAML clean โ no extra echo commands, no nonsense
Unless you have a very specific reason not to, this should be your default.
๐ฉ Dynamic Variables: Born to Be Wild
Now what if your variables arenโt so chill? Maybe you calculate something in one step and need to pass it to another โ a file path, a version number, an API token from a secret backend ritual…
Thatโs when you reach for the slightly moreโฆ creative option:
๐ง $GITHUB_ENV to the rescue
- name: Set dynamic environment vars
run: |
echo "BUILD_DATE=$(date +%F)" >> $GITHUB_ENV
echo "RELEASE_TAG=v1.$(date +%s)" >> $GITHUB_ENV
- name: Use them later
run: echo "Tag: $RELEASE_TAG built on $BUILD_DATE"
What it does:
- Persists the variables across steps
- Works well when values are calculated during the run
- Makes you feel powerful
๐ช Fancy Bonus: Heredoc Style
If you like your YAML with a side of Bash wizardry:
- name: Set vars with heredoc
run: |
cat <<EOF >> $GITHUB_ENV
FOO=bar
BAZ=qux
EOF
Because sometimes, you just want to feel fancy.
๐ตโ๐ซ What Not to Do (Unless You Really Mean It)
- name: Set env with export
run: |
export FOO=bar
echo "FOO is $FOO"
This only works within that step. The minute your pipeline moves on, FOO is gone. Poof. Into the void. If thatโs what you want, fine. If not, donโt say I didnโt warn you.
๐ง TL;DR โ The Cheat Sheet
| Scenario | Best Method |
|---|---|
| Static variable used in all steps | env at the job level โ
|
| Static variable used in one step | env at the step level |
| Dynamic value needed across steps | $GITHUB_ENV โ
|
| Dynamic value only needed in one step | export (but donโt overdo it) |
| Need to show off with Bash skills | cat <<EOF >> $GITHUB_ENV ๐ |
๐งช My Use Case: Ansible FTW
In my setup, I wanted to use:
ANSIBLE_CONFIG=ansible.cfg
ANSIBLE_STDOUT_CALLBACK=yaml
These are rock-solid, boringly consistent values. So instead of writing this in every step:
- name: Set env
run: |
echo "ANSIBLE_CONFIG=ansible.cfg" >> $GITHUB_ENV
I now do this:
jobs:
deploy:
runs-on: ubuntu-latest
env:
ANSIBLE_CONFIG: ansible.cfg
ANSIBLE_STDOUT_CALLBACK: yaml
steps:
...
Cleaner. Simpler. One less thing to trip over when Iโm debugging at 2am.
๐ฌ Final Thoughts
Environment variables in GitHub Actions arenโt hard โ once you know the rules of the game. Use env for the boring stuff. Use $GITHUB_ENV when you need a little dynamism. And remember: if youโre writing export in step after step, something probably smells.
Got questions? Did I miss a clever trick? Want to tell me my heredoc formatting is ugly? Hit me up in the comments or toot at me on Mastodon.
โ๏ธ Posted by Amedee, who loves YAML almost as much as dancing polskas.
๐ฅ Because good CI is like a good dance: smooth, elegant, and nobody falls flat on their face.
๐ป Scheduled to go live on 20 August โ just as Boombalfestival kicks off. Because why not celebrate great workflows and great dances at the same time?