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?