Ansible Environment Variables — Set and Use in Playbooks

Introduction

Environment variables configure how programs behave — database URLs, API keys, proxy settings, PATH, and more. Ansible lets you set environment variables at the task, play, block, or role level using the environment keyword. This guide covers every technique for managing environment variables in your automation.

Task-Level Environment

---
- name: Environment variable examples
  hosts: all
  tasks:
    - name: Run command with custom env
      ansible.builtin.command:
        cmd: /opt/app/deploy.sh
      environment:
        APP_ENV: production
        DATABASE_URL: "postgresql://db:5432/myapp"
        LOG_LEVEL: info

    - name: Build with custom PATH
      ansible.builtin.command:
        cmd: make build
        chdir: /opt/app
      environment:
        PATH: "/opt/go/bin:/usr/local/bin:{{ ansible_env.PATH }}"
        GOPATH: /opt/go

Play-Level Environment

Set once, applies to all tasks in the play:

---
- name: Deploy application
  hosts: webservers
  become: true
  environment:
    APP_ENV: production
    RAILS_ENV: production
    NODE_ENV: production

  tasks:
    - name: Run migrations
      ansible.builtin.command:
        cmd: bundle exec rails db:migrate
        chdir: /opt/app
      # Inherits APP_ENV, RAILS_ENV, NODE_ENV

    - name: Compile assets
      ansible.builtin.command:
        cmd: bundle exec rails assets:precompile
        chdir: /opt/app
      # Also inherits the play environment

Block-Level Environment

    - name: Java tasks with JAVA_HOME
      environment:
        JAVA_HOME: /usr/lib/jvm/java-17-openjdk
        PATH: "/usr/lib/jvm/java-17-openjdk/bin:{{ ansible_env.PATH }}"
      block:
        - name: Build Java app
          ansible.builtin.command:
            cmd: mvn clean package
            chdir: /opt/app

        - name: Run tests
          ansible.builtin.command:
            cmd: mvn test
            chdir: /opt/app

Using Variables for Environment

---
- name: Environment from variables
  hosts: webservers
  vars:
    app_environment:
      DATABASE_URL: "{{ vault_database_url }}"
      REDIS_URL: "{{ vault_redis_url }}"
      SECRET_KEY: "{{ vault_secret_key }}"
      APP_ENV: "{{ env | default('production') }}"

  environment: "{{ app_environment }}"

  tasks:
    - name: Start application
      ansible.builtin.systemd:
        name: myapp
        state: restarted

Proxy Configuration

---
- name: Configure hosts behind proxy
  hosts: all
  become: true
  environment:
    http_proxy: "http://proxy.example.com:3128"
    https_proxy: "http://proxy.example.com:3128"
    no_proxy: "localhost,127.0.0.1,.example.com"

  tasks:
    - name: Install packages (uses proxy)
      ansible.builtin.apt:
        name: nginx
        state: present
        update_cache: true

    - name: Download file (uses proxy)
      ansible.builtin.get_url:
        url: https://example.com/file.tar.gz
        dest: /tmp/file.tar.gz

Persistent Environment Variables

Set permanent environment variables on the target:

    # System-wide via /etc/environment
    - name: Set system environment variable
      ansible.builtin.lineinfile:
        path: /etc/environment
        regexp: '^APP_ENV='
        line: 'APP_ENV=production'

    # Per-user via .bashrc
    - name: Set user environment variable
      ansible.builtin.lineinfile:
        path: "/home/{{ app_user }}/.bashrc"
        regexp: '^export APP_ENV='
        line: 'export APP_ENV=production'

    # Via profile.d (all users)
    - name: Create app profile
      ansible.builtin.copy:
        content: |
          export APP_ENV=production
          export APP_HOME=/opt/app
          export PATH=$APP_HOME/bin:$PATH
        dest: /etc/profile.d/myapp.sh
        mode: '0644'

    # Systemd service environment
    - name: Create systemd override
      ansible.builtin.copy:
        content: |
          [Service]
          Environment="DATABASE_URL={{ vault_database_url }}"
          Environment="REDIS_URL={{ vault_redis_url }}"
        dest: /etc/systemd/system/myapp.service.d/env.conf
        mode: '0600'
      notify: Restart myapp

Reading Remote Environment

    # ansible_env contains the remote user's environment
    - name: Show remote PATH
      ansible.builtin.debug:
        msg: "Remote PATH: {{ ansible_env.PATH }}"

    - name: Show all remote env vars
      ansible.builtin.debug:
        var: ansible_env

    # Read specific env var from remote
    - name: Check JAVA_HOME
      ansible.builtin.debug:
        msg: "JAVA_HOME is {{ ansible_env.JAVA_HOME | default('NOT SET') }}"

Controller Environment Variables

# Pass env vars to ansible-playbook
APP_VERSION=2.0 ansible-playbook deploy.yml

# Access in playbook with lookup
    - name: Use controller env var
      ansible.builtin.debug:
        msg: "Deploying version {{ lookup('env', 'APP_VERSION') }}"

    # Common pattern: fall back to default
    - name: Get version from env or default
      ansible.builtin.set_fact:
        deploy_version: "{{ lookup('env', 'APP_VERSION') | default('latest', true) }}"

Environment File Pattern

    # Load from .env file
    - name: Read .env file
      ansible.builtin.slurp:
        src: /opt/app/.env
      register: env_file

    - name: Parse .env file
      ansible.builtin.set_fact:
        app_env: "{{ dict(env_file.content | b64decode | split('\n') | select | map('split', '=', 1) | list) }}"

Troubleshooting

IssueSolution
Env var not visibleenvironment only applies to that task's process
PATH not workingAppend to existing: "{{ ansible_env.PATH }}"
Proxy not usedSet both http_proxy and https_proxy (lowercase)
become resets envUse become_flags: '-E' to preserve environment
Env var has quotesDon't double-quote: APP_ENV: production not APP_ENV: "'production'"

Best Practices

  1. Use Vault for secrets — never hardcode API keys or passwords
  2. Define at play level for consistency — avoid per-task duplication
  3. Use variables for environments — environment: "{{ app_env }}" with per-env var files
  4. Append to PATH — don't replace: /opt/bin:{{ ansible_env.PATH }}
  5. Use systemd Environment files for services — not .bashrc

Conclusion

The environment keyword gives you fine-grained control over process environment variables at every level — task, block, play, and role. Use it for proxies, build tools, application configuration, and anything that needs environment context. For persistent variables, use lineinfile with /etc/environment, profile.d, or systemd overrides.