envtab (typed envt\t) aims to be your goto tool for working with environment variables. Organize sets of environment variables into loadouts. A loadout is a collection of environment variables that can be exported into the shell. Loadouts are named, optionally tagged, and can include a description. envtab stores these loadouts in your data directory. envtab loadouts can also be enabled on shell login.
- Installation
- Usage
- Environment Variables
- Encrypting Sensitive Values
- Importing Loadouts and dotenv Files
- Generating CLI documentation
- TODO
- Go 1.25 or later
- Git
git clone https://github.com/gmherb/envtab.git
cd envtab; make installInstalls to /usr/local/bin/envtab
make build
./envtab --versionComplete documentation for all envtab commands:
envtab add- Add an entry to a envtab loadoutenvtab cat- Concatenate envtab loadouts to stdoutenvtab edit- Edit envtab loadoutenvtab export- Export envtab loadout(s)envtab import- Import environment variables or loadoutsenvtab list- List all envtab loadoutsenvtab login- Export all login loadoutsenvtab make- Make loadout from a templateenvtab remove- Remove envtab loadout(s)envtab show- Show active loadouts
See also: envtab.md for top-level usage and flags.
envtab searches for configuration files in the following order (first found is used):
--configflag (explicit override)ENVTAB_CONFIGenvironment variable (explicit override)- Project config:
.envtab.yamlin current directory, walking up the directory tree - User config:
$XDG_CONFIG_HOME/envtab/envtab.yaml(defaults to$HOME/.config/envtab/envtab.yaml) - System config:
/etc/envtab.yaml
The data directory (where loadouts and templates are stored) is determined by:
ENVTAB_DIRenvironment variable (if set, overrides path selection)- XDG path:
$XDG_DATA_HOME/envtab(defaults to$HOME/.local/share/envtab)
envtab uses XDG Base Directory paths:
- Data:
$XDG_DATA_HOME/envtab(defaults to$HOME/.local/share/envtab) - Config:
$XDG_CONFIG_HOME/envtab/envtab.yaml(defaults to$HOME/.config/envtab/envtab.yaml) - Cache:
$XDG_CACHE_HOME/envtab/tmp/(defaults to$HOME/.cache/envtab/tmp/)
ENVTAB_DIR: Override the data directory locationENVTAB_CONFIG: Override the config file locationXDG_DATA_HOME: Used for data directory (defaults to$HOME/.local/share)XDG_CONFIG_HOME: Used for config file location (defaults to$HOME/.config)XDG_CACHE_HOME: Used for temporary/cache files (defaults to$HOME/.cache)
envtab supports environment variables in values and PATH as a key.
Environment variables are fully supported in values. Any environment variable can be referenced using $VARNAME syntax, and it will be automatically expanded during export.
Variables within a loadout entry value will be expanded before export:
$ envtab cat example
metadata:
createdAt: "2025-11-23T22:59:13-05:00"
loadedAt: "2025-11-23T22:59:13-05:00"
updatedAt: "2025-11-23T23:08:32-05:00"
login: false
tags: []
description: ""
entries:
CONFIG_DIR: $HOME/conf
PROJECT_ROOT: $HOME/projects/$PROJECT_NAME
LOG_DIR: $CONFIG_DIR/logs
PATH: $PATH:/other/bin
# Export automatically expands all variables
$ envtab export example
export CONFIG_DIR=/home/gmherb/conf
export PROJECT_ROOT=/home/gmherb/projects/myproject
export LOG_DIR=/home/gmherb/conf/logs
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/go/bin:/other/bin
# Source the export to set variables
$ $(envtab export example)
# Variables are expanded
$ env|grep CONFIG_DIR
CONFIG_DIR=/home/gmherb/conf
$ env|grep PROJECT_ROOT
PROJECT_ROOT=/home/gmherb/projects/myproject
$ env|grep LOG_DIR
LOG_DIR=/home/gmherb/conf/logs
If a referenced environment variable is unset or empty, the entry will be skipped during export to prevent setting empty values.
The PATH environment variable is treated specially to track entries added in different loadouts. All other keys (!PATH) will be treated as normal. Values are not injected into environment for consecutive loadout exports.
NOTE: To utilize multiple entries of the same KEY such as PATH, you must utilize multiple loadouts. A single loadout cannot have duplicate keys.
When using add, environment variables will be subjected to shell variable/parameter expansion. You must escape the $ to prevent shell expansion and preserve the variable reference:
# Without escaping - shell expands $PATH before envtab sees it
$ envtab add testld PATH=$PATH:/other/bin
$ envtab cat testld | grep PATH
PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/go/bin:/other/bin
# With escaping - preserves $PATH for expansion during export
$ envtab add testld PATH=\$PATH:/other/bin
$ envtab cat testld | grep PATH
PATH: $PATH:/other/bin
# Same applies to other environment variables in values
$ envtab add example CONFIG_DIR=\$HOME/conf
$ envtab cat example | grep CONFIG_DIR
CONFIG_DIR: $HOME/conf
When editing the loadout configuration directly, you can use $VARNAME syntax without escaping:
$ envtab edit testld
----
metadata:
createdAt: "2025-11-21T19:21:06-05:00"
loadedAt: "2025-11-21T19:21:06-05:00"
updatedAt: "2025-11-21T19:25:07-05:00"
login: false
tags: []
description: ""
entries:
PATH: $PATH:/other/bin
CONFIG_DIR: $HOME/conf
$ envtab export testld
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/go/bin:/other/bin
export CONFIG_DIR=/home/gmherb/conf
$ $(envtab export testld)
$ envtab show
testld -------------------------------------------------------------- [ 2 / 2 ]
PATH=$PATH:/other/bin
CONFIG_DIR=$HOME/conf
envtab supports encrypting sensitive values using SOPS (Secrets OPerationS). This allows you to securely store secrets like API keys, passwords, and tokens in your loadouts.
- Install SOPS: https://github.com/getsops/sops
- Configure SOPS with your preferred encryption backend (AWS KMS, GCP KMS, Azure Key Vault, age, PGP, etc.)
- Set up your
.sops.yamlconfiguration file (optional, but recommended)
Configure SOPS by creating a .sops.yaml file in your project root or home directory:
creation_rules:
- path_regex: envtab-stdin-override
kms: 'arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012'
pgp: >-
FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4
- kms: 'arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012'
pgp: >-
FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4For more details, see SOPS_INTEGRATION.md.
The -e or --encrypt-value flag encrypts individual values with SOPS:
$ envtab add production -e API_KEY=sk_live_1234567890abcdef
$ envtab add production -e DB_PASSWORD=super_secret_password
When you view the loadout, encrypted values are hidden by default:
$ envtab cat production
metadata:
createdAt: "2025-11-26T00:00:00Z"
...
entries:
API_KEY: SOPS:value: ENC[AES256_GCM,data:...,iv:...,tag:...,type:str]
sops:
gcp_kms: [...]
...
DB_PASSWORD: SOPS:value: ENC[AES256_GCM,data:...,iv:...,tag:...,type:str]
sops:
gcp_kms: [...]
...
You can also encrypt entire loadout files with SOPS using the --encrypt-file flag (or -f):
$ envtab add secrets --encrypt-file API_KEY=mykey
This encrypts the entire file, including metadata. The file can be edited directly with sops:
$ sops $ENVTAB_DIR/secrets.yaml # or ~/.local/share/envtab/secrets.yaml by default
NOTE: File encryption will be faster if multiple encrypted values exist in a single loadout.
To view decrypted sensitive values, use the -d or --decrypt flag with the show command:
$ envtab show production
production -------------------------------------------------------- [ 2 / 2 ]
API_KEY=***encrypted***
DB_PASSWORD=***encrypted***
$ envtab show production -d
production -------------------------------------------------------- [ 2 / 2 ]
API_KEY=sk_live_1234567890abcdef
DB_PASSWORD=super_secret_password
Encrypted values are automatically decrypted when exporting:
$ envtab export production
export API_KEY=sk_live_1234567890abcdef
export DB_PASSWORD=super_secret_password
When editing a loadout with encrypted values, they are automatically decrypted for editing and re-encrypted when saved:
$ envtab edit production
# Values appear as plaintext in the editor
# After saving, they are automatically re-encrypted
envtab imports entire loadouts from .yaml files. It also can import variables from .env files.
# Merge a .env into an existing/new loadout
envtab import myloadout ./config.env
# Replace/create a loadout from YAML
envtab import myloadout ./prod.yaml
# Merge .env from URL into an existing/new loadout
envtab import myloadout --url https://raw.githubusercontent.com/org/repo/branch/config.env
# Replace/create a loadout from YAML at a URL
envtab import myloadout --url https://raw.githubusercontent.com/org/repo/branch/loadouts/prod.yaml
envtab cat myloadout --output ./envtab-loadout-repo/myloadout.yaml
You can then commit and push these YAML files to GitHub or another Git host and share them with your team.
This project includes a small tool that uses Cobra's doc package to generate Markdown docs for all commands.
-
Generate docs into the
docs/directory:make docs
This runs go run ./tools/gen-docs.go and produces per-command Markdown files and a top-level docs/envtab.md that reflect the current CLI.
- Add config option to show raw sops data vs
***encrypted***string in showCmd - Should we modify the prefix (SOPS:) to something less likely to occur in values?
- SOPS:exec-env - execute a command with decrypted values inserted into the environment
- Add support or re-implement. Reimplementation would be best as can support all envtab loadouts
- Add --raw to loginCmd. This will place actual export entries inside of a shell script to be sourced from profile script instead of calling envtab.
- Safer, faster, but lacks encryption at rest.
- Also supports all environment values in entries as they will be evaluated on source.
- However, syncing login requires checking for diffs after every edit, add, and import (make should have empty values from a template unless we are supporting values in templates)
- sync can be manual for first implementation.
envtab login status --sync- but end goal is
--syncflag on edit, add, import, and make which triggers sync process. - or make it automatic for simplicity
- but end goal is
- sync can be manual for first implementation.
- --raw should be utilized with either --enable or --disable. Ignored if --status or enable/disable are omitted.
- --status should now include mode (raw|command substitution)
- Add loadout order/priority/number to support specific load order in case entries build upon environment variable expansion
- Add additional backends in addition to default (file backend).
- File (Default)
- Vault
- Add ability to import/export various backends (import|export subCmd)
- Vault, S3, GCS
- Fix the color output with show --all
- Support
--keyand--valuein showCmd to locate specific vars without using$(echo $KEY)or$(env|grep $VAL):- Show env var matching
--key - Show env var matching
--value
- Show env var matching
- Support
--allto show all envtab entries. Not just those that are active. - Allow passing filter/pattern arg to the listCmd. (done w/ glob)
- Add support for PATH environment variable (done)
- Fix show for PATH environment variable (done)
- Fix Active/Total spacing in
lsoutput when counts are double, or triple digits. (done) - Implement
-s|--sensitiveoption to the addCmd to optionally encrypt values. (done)- Support: GCP KMS, AWS KMS, GPG(PGP)
- Piggy back off sops? It already supports all providers
- In edit subcommand, ensure no duplicate keys (otherwise it will be overwritten) (done)
- edit fails when loadout does not exist (fixed)
- Add support for templates with the mk command. Set user defined for matches before utilizing included templates.
- templates can be .env files.
- include templates for common things: AWS|GCP|Azure, Vault, GIT|GITHUB|GITLAB, PGSQL, etc.
- Support importing templates (.env)
- Support environment variables in show; exported with eval $(envtab export loadout)
- Can we resolve all environment variables like we do with PATH? --raw is a workaround for using other environment variables
