The problem every DevOps engineer faces
It's 3 AM. Production is down. You need to debug NOW.
What do you do?
$ export AWS_PROFILE=production
$ kubectx production-cluster
$ kubectl config set-context --current --namespace=api
$ ssh -f -N -L 5432:db.prod:5432 bastion.prod
$ export VAULT_ADDR=https://vault.prod
$ # ... wait, which profile am I using again?
6+ commands. Every. Single. Time.
And that's assuming you remember them all correctly. One wrong command at 3 AM and you've just deployed to the wrong cluster. Or worse, accidentally modified production with dev credentials still active.
Why kubectx isn't enough
Don't get me wrong, kubectx is great for what it does. But it only solves one piece of the puzzle: Kubernetes contexts.
What about:
- AWS/GCP/Azure credentials?
- SSH tunnels to databases?
- VPN connections?
- Environment variables?
- Vault tokens?
- Git user configs?
- Browser profiles for SSO?
You're still managing 7+ different tools and hoping they all stay in sync.
Meet ctx: one command for your entire environment
ctx treats your entire DevOps environment as a single switchable context:
$ ctx use production
⚠ Switching to PRODUCTION environment: production
Type 'yes' to confirm: yes
✓ AWS profile: production (us-east-1)
✓ Kubernetes: prod-cluster/default
✓ VPN connected: wireguard (wg0)
✓ Tunnel postgres: localhost:5432 → db.prod:5432
✓ Tunnel redis: localhost:6379 → cache.prod:6379
✓ Browser: Chrome (Production Profile)
✓ Environment variables loaded
✓ Secrets loaded from Vault
[ctx: production] $
One command. Everything switches.
Real-world comparison
Let's see how different tools stack up for a typical multi-cloud, multi-environment setup:
| Feature | kubectx | direnv | aws-vault | ctx |
|---|---|---|---|---|
| Kubernetes context | ✅ | ❌ | ❌ | ✅ |
| Multi-cloud (AWS/GCP/Azure) | ❌ | ❌ | AWS only | ✅ |
| Environment variables | ❌ | ✅ | ❌ | ✅ |
| SSH tunnels | ❌ | ❌ | ❌ | ✅ |
| VPN management | ❌ | ❌ | ❌ | ✅ |
| Production safeguards | ❌ | ❌ | ❌ | ✅ |
| Auto-fetch K8s credentials | ❌ | ❌ | ❌ | ✅ |
| Context inheritance | ❌ | ❌ | ❌ | ✅ |
| Variable interpolation | ❌ | ✅ | ❌ | ✅ |
| Secrets management | ❌ | ❌ | ❌ | ✅ |
| Browser profiles | ❌ | ❌ | ❌ | ✅ |
| URL quick links | ❌ | ❌ | ❌ | ✅ |
What makes ctx different
1. Production safeguards (because 3 AM you is dangerous)
$ ctx use production
⚠ Switching to PRODUCTION environment: production
Type 'yes' to confirm: yes
✓ Context 'production' is now active.
⚠ You are now in PRODUCTION environment.
You have to WANT to be in production. No more "oops, wrong cluster".
The production environment type requires explicit confirmation. No flags, no shortcuts, just type "yes" to confirm you really want to access production.
2. Auto-fetch Kubernetes credentials from cloud providers
ctx automatically fetches K8s credentials when you switch contexts:
# Your context config
kubernetes:
context: my-cluster
namespace: default
eks:
cluster: my-cluster
region: us-west-2
When you switch, ctx automatically runs:
-
EKS:
aws eks update-kubeconfig --name <cluster> --region <region> -
GKE:
gcloud container clusters get-credentials <cluster> --zone <zone> --project <project> -
AKS:
az aks get-credentials --name <cluster> --resource-group <rg>
No more manual credential refreshes. It just works.
3. Context inheritance + variable interpolation for DRY configs
Managing 50 similar clusters? Combine inheritance with variables:
# base-acme.yaml (abstract base)
name: acme-base
abstract: true
cloud: acme
kubernetes:
context: "acme-${CLUSTER_NAME}-cluster"
kubeconfig: "~/clusters/acme/${CLUSTER_NAME}/config"
env:
API_URL: "https://api.${CLUSTER_NAME}.acme.com"
CLUSTER: "${CLUSTER_NAME}"
# daisy.yaml (extends base + sets variable)
name: daisy
extends: acme-base
env:
CLUSTER_NAME: daisy
# eve.yaml (extends base + sets variable)
name: eve
extends: acme-base
env:
CLUSTER_NAME: eve
Two clusters. 8 lines total. Variables are expanded after inheritance merging, so you can reference them in the base config.
4. SSH tunnels through bastion with auto-reconnect
Tunnels that actually stay up, all go through your bastion host:
ssh:
bastion:
host: bastion.example.com
user: deploy # defaults to $USER if not specified
port: 22
tunnels:
- name: postgres
remote_host: db.internal
remote_port: 5432
local_port: 5432
timeout: 10s # configurable timeout
- name: redis
remote_host: cache.internal
remote_port: 6379
local_port: 6379
All tunnels go through the bastion. ctx manages the SSH connection lifecycle. When you switch contexts, old tunnels are cleaned up. No more orphaned SSH processes.
$ ctx tunnel list
Active tunnels for context 'staging':
postgres: localhost:5432 → db.internal:5432 (PID: 12345)
redis: localhost:6379 → cache.internal:6379 (PID: 12346)
$ ctx tunnel down postgres
✓ Tunnel 'postgres' stopped
$ ctx tunnel up postgres
✓ Tunnel 'postgres' started
If a local port is already in use, ctx will detect the conflict and let you know.
5. Secrets from multiple secret stores
Fetch secrets from any supported secret manager:
secrets:
# Bitwarden
bitwarden:
DB_PASSWORD: "Production DB#password"
API_KEY: "Production API Key"
# 1Password
onepassword:
SMTP_PASSWORD: "SendGrid#password"
# HashiCorp Vault
vault:
CONSUL_TOKEN: "operations/consul#http_token"
NOMAD_TOKEN: "operations/nomad#token"
# AWS Secrets Manager
aws_secrets_manager:
DATADOG_API_KEY: "prod/datadog#api_key"
# AWS SSM Parameter Store
aws_ssm:
DATABASE_URL: "/prod/database/url"
# GCP Secret Manager
gcp_secret_manager:
SENTRY_DSN: "prod-sentry-dsn"
# Secret store configs
bitwarden:
auto_login: true
vault:
address: https://vault.prod.internal
namespace: operations
When you switch contexts, ctx fetches all configured secrets and exports them as environment variables. No hardcoded credentials.
All secret stores support automated login/authentication:
- Bitwarden: auto-login with SSO support (org_identifier)
- 1Password: biometric/system authentication
- Vault: token management with auto-refresh
- AWS: SSO, aws-vault integration
- GCP: gcloud authentication with SSO
- Azure: az CLI authentication with SSO
6. VPN support (OpenVPN, WireGuard, Tailscale)
Auto-connect to VPN when switching:
# OpenVPN
vpn:
openvpn:
config: /etc/openvpn/production.ovpn
auth_file: /etc/openvpn/auth.txt
# WireGuard
vpn:
wireguard:
interface: wg0
config: /etc/wireguard/prod.conf
# Tailscale
vpn:
tailscale:
network: prod-network
# Custom command
vpn:
command: /usr/local/bin/connect-prod-vpn.sh
VPN connects automatically when you switch contexts, disconnects when you deactivate.
7. Browser profiles (Chrome & Firefox)
Launch the right browser profile for each context:
browser:
type: chrome
profile: Production Profile # Chrome profile name
# Or Firefox
browser:
type: firefox
profile: work-prod # Firefox profile name
Perfect for SSO workflows. Each context can have its own browser profile with saved credentials, cookies, and sessions.
$ ctx open https://console.aws.amazon.com
# Opens URL in Chrome with "Production Profile"
8. Multi-cloud support
Switch between AWS, GCP, and Azure seamlessly:
# AWS context
aws:
profile: myapp-prod
region: us-east-1
sso: true
# GCP context
gcp:
project: myapp-prod
region: us-central1
zone: us-central1-a
# Azure context
azure:
subscription: myapp-prod
tenant: 12345-abcde
resource_group: production
One interface. Multiple clouds.
9. Quick links for context-specific URLs
Store frequently accessed URLs for each context:
urls:
dashboard: https://grafana.prod.internal/d/prod-overview
logs: https://kibana.prod.internal/app/logs
console: https://console.aws.amazon.com
docs: https://docs.prod.internal
Access them quickly:
$ ctx open dashboard
# Opens https://grafana.prod.internal/d/prod-overview in context browser
$ ctx open logs
# Opens https://kibana.prod.internal/app/logs
Perfect for keeping all your production links organized and context-aware.
10. Smart defaults
SSH user not specified? ctx defaults to your OS user, just like SSH does:
ssh:
bastion:
host: bastion.internal
# user: defaults to $USER
port: 22
Teams can share base contexts without hardcoding usernames. Everyone uses their own username automatically.
Real-world use cases
Multi-environment juggling
$ ctx list
NAME ENVIRONMENT CLOUD ORCHESTRATION
myapp-dev development aws kubernetes
myapp-staging staging aws kubernetes
myapp-prod production aws kubernetes
$ ctx use myapp-staging
✓ Context 'staging' is now active
$ ctx use myapp-prod
⚠ Type 'yes' to confirm: yes
✓ Context 'production' is now active
Multi-client consultant
Managing 10 clients with different clouds, clusters, and tools?
$ ctx list
NAME ENVIRONMENT CLOUD ORCHESTRATION
client-a-prod production aws kubernetes
client-b-prod production gcp kubernetes
client-c-staging staging azure nomad
$ ctx use client-b-prod
⚠ Type 'yes' to confirm: yes
✓ GCP project: client-b-production
✓ GKE cluster: prod-cluster-eu
✓ VPN connected
No confusion. No mistakes.
Incident response at 3 AM
[03:42] ALERT: Production API down!
[03:43] $ ctx use api-prod
[03:43] ⚠ Type 'yes' to confirm: yes
[03:43] ✓ AWS profile: production
[03:43] ✓ Kubernetes: prod-cluster/api
[03:43] ✓ VPN connected
[03:43] ✓ Tunnels connected
[03:43] ✓ Secrets loaded
[03:44] $ kubectl get pods
[03:44] # Debug and fix...
[03:56] $ kubectl apply -f hotfix.yaml
[03:56] ✓ Incident resolved
[03:57] $ ctx deactivate
[03:57] ✓ Safely exited production
Under pressure. No mistakes. Fast.
How it works
ctx is a single Go binary with zero dependencies. It reads YAML configs from ~/.config/ctx/contexts/:
name: myapp-prod
description: "MyApp Production"
environment: production
aws:
profile: prod
region: us-west-2
kubernetes:
context: prod-cluster
namespace: default
eks:
cluster: prod-cluster
region: us-west-2
ssh:
bastion:
host: bastion.prod.internal
user: deploy
tunnels:
- name: postgres
remote_host: db.internal
remote_port: 5432
local_port: 5432
vpn:
wireguard:
interface: wg0
config: /etc/wireguard/prod.conf
browser:
type: chrome
profile: Production
urls:
dashboard: https://grafana.prod.internal
console: https://console.aws.amazon.com
logs: https://kibana.prod.internal
secrets:
vault:
NOMAD_TOKEN: "operations/nomad#token"
env:
API_URL: https://api.prod.example.com
LOG_LEVEL: info
When you run ctx use myapp-prod, it:
- Fetches K8s credentials from AWS (if configured)
- Switches AWS profile
- Switches Kubernetes context and namespace
- Connects VPN
- Establishes SSH connection to bastion
- Starts SSH tunnels through bastion
- Fetches secrets from Vault/Bitwarden/etc
- Exports environment variables
- Makes URL quick links available
- Updates your shell prompt
One command. Everything switches.
Getting started
Install
# macOS / Linux
curl -fsSL https://github.com/vlebo/ctx/releases/latest/download/install.sh | sh
Setup shell integration
Add to ~/.zshrc or ~/.bashrc:
eval "$(ctx shell-hook zsh)" # for Zsh
eval "$(ctx shell-hook bash)" # for Bash
Create your first context
ctx init my-dev
Or manually create ~/.config/ctx/contexts/my-dev.yaml:
name: my-dev
environment: development
aws:
profile: dev
region: us-west-2
kubernetes:
context: dev-cluster
namespace: default
env:
API_URL: https://api.dev.example.com
Switch
ctx use my-dev
That's it.
Why ctx vs everything else?
| Tool combo | Commands needed | Production safety | Team sharing |
|---|---|---|---|
| kubectx + aws-vault + custom scripts | 6+ commands | ❌ No | ❌ No |
| direnv + kubectx + aws-vault | 3-4 commands | ❌ No | ⚠️ Git only |
| ctx | 1 command | ✅ Yes | ✅ Yes (coming soon) |
ctx isn't just replacing one tool, it's replacing your entire context-switching workflow.
TL;DR
ctx combines:
- ✅ Variable interpolation (
${VAR}in configs) - ✅ Auto-fetch K8s credentials from EKS/GKE/AKS
- ✅ Additional cloud parameter for non-standard providers
- ✅ Context inheritance to avoid duplication
- ✅ Production environment safeguards
- ✅ Multi-cloud support (AWS/GCP/Azure) with SSO
- ✅ VPN management (OpenVPN/WireGuard/Tailscale)
- ✅ SSH tunnels through bastion
- ✅ Secrets from 6 secret stores with auto-auth
- ✅ Browser profile switching (Chrome/Firefox)
- ✅ URL quick links for dashboards/consoles
- ✅ Git user configuration
What's next?
Roadmap for 2026:
-
Q2 2026:
- ✅ Context inheritance + variable interpolation
- ✅ Auto-fetch K8s credentials
- 🚧 Zsh plugin with completions
- 🚧 GitHub Actions integration
-
Q3 2026:
- ctx-cloud for team context sharing
- Web UI for context management
- Docker context integration
- Plugin system for custom integrations
Coming soon: ctx-cloud
Team context sharing and centralized management:
$ ctx cloud login https://ctx-cloud-url
$ ctx cloud sync production
✓ Context 'production' synced
Features:
- Team context sharing
- Audit logs
- Web UI
- SAML/SSO integration
- Centralized secrets management
Beta coming Q3 2026. Currently under active development. Star the repo to stay updated!
Try it now
curl -fsSL https://github.com/vlebo/ctx/releases/latest/download/install.sh | sh
- GitHub: github.com/vlebo/ctx
- Docs: vlebo.github.io/ctx
- License: MIT
- Language: Go
- Dependencies: Zero
Have you tried ctx? What's your current context-switching setup? Let me know in the comments!
Found this useful? ⭐ Star the repo: github.com/vlebo/ctx
Top comments (8)
Exactly what's needed to manage multiple levels of organisations and/or clients!
Also, a magnet for Vedrans I see :D
Yes, individuals or agencies. Everyone can benefit.
Keep up the good work, can't wait for the Zsh plugin and completions.
Zsh plugin is already in the making, but will be released a bit later :) Thanks!
Truly remarkable tool, saves ton of manual context switching and I can no longer drop prod databases by accidentally running commands in unwanted context. Great work!
Thanks!
Looks awesome!
Thank you!