.bashrc series

.bashrc.local

How to support personal customization without breaking config-as-code

May 26, 2026 ~4 min
bashprocessansibleteam

Every config-management deployment has a tension nobody talks about: managed files take away the engineer's ability to customize. The first time someone runs ansible-playbook bash-env.yml and watches their carefully-tuned ~/.bashrc get replaced with the company-standard version — with their personal aliases gone — they will fight the system. Either by disabling Ansible runs, or by maintaining a side document with "stuff I need to re-add after every deploy."

The fix is a single convention: a managed system sources an unmanaged sibling file.

# In the managed ~/.bashrc — the last line:
[ -f "$HOME/.bashrc.local" ] && . "$HOME/.bashrc.local"

That's the entire contract. Anything in ~/.bashrc.local is the engineer's. Ansible never touches it. The managed bashrc just sources it after everything else, so per-user customizations win.

What goes in .bashrc.local

By convention:

What does NOT go in .bashrc.local

The Ansible side — create the stub, never overwrite

- name: Create per-user .bashrc.local stub (untouched on rerun)
  copy:
    dest: "{{ user_home }}/.bashrc.local"
    content: |
      # ~/.bashrc.local — your personal overrides.
      # This file is never overwritten by Ansible. Drop personal aliases,
      # env vars, or function defs here. Sourced last, so your settings
      # win over the fleet defaults.
    owner: "{{ user }}"
    group: "{{ user }}"
    mode: '0600'
    force: no   # critical — do NOT overwrite if it exists

The force: no is the entire design pattern in one keyword. On first run, the stub is created. On every subsequent run, Ansible sees the file exists and leaves it alone — even if its contents have drifted wildly from the stub.

Why the discipline matters beyond bash

This pattern — managed file references an unmanaged sibling — works for almost any config-as-code scenario where you also want to permit per-host or per-user variation:

In every case, the managed system retains control of structure and defaults; the local file gets the last word on overrides. Engineers stop fighting the system because the system respects them.

Closing the series

Seven articles. The single thread running through all of them: ~/.bashrc is not a personal scratch file. It's the surface where your operating environment, your safety controls, your secrets policy, and your team's process all meet. Treat it that way and the leverage compounds across every shell you start for the rest of your career.

The hidden tiger has teeth. I hope this series gave you a few of them.

A managed file that respects its unmanaged sibling is the difference between config-as-code and config-as-tyranny.

Back to the series index: .bashrc. Or browse other writing: Engineering · Field Notes.

All .bashrc articles