ebman
A k9s-style terminal UI for AWS Elastic Beanstalk.
Browse environments, drill into events / instances / metrics / queue / config, stream CloudWatch logs, edit env vars / option settings, deploy new versions — all without leaving the terminal.
Highlights
- Live env table with sort, filter, group-by-app, health sparkline (trend window auto-labelled), severity tints, mouse support.
Tabcycles between Envs and Apps scope. - Drill-down per env — opens to a Health rollup tab (drillable:
j/kwalks items,Enterjumps to source). Other tabs: Events (regex-searchable), Instances (with health causes + embedded SSM shell), Metrics (CloudWatch line charts; custom metrics with arbitrary dimensions), Queue (Worker tier; main + DLQ stats with viewer), Logs (one-shot snapshot or realtail -ffrom CW Logs), Config (tags + env vars read-only, plus cost estimate). - Red-env triage —
:why(or!on the selected env) opens a four-section diagnostic overlay: recent events, alarms, instance health, recent deploys. Worker envs also get a main + DLQ peek. Updating envs are labelled with the kind of update in flight (deploy / config / scale); an env with active CW alarms no longer renders as plain green "Ready". Worker envs with a non-empty DLQ flip Red even when EB calls them healthy. - Daily-driver write surface — env vars (
:env set/unset), tags (:tag/:untag), version deploy from existing label or local zip / S3 (:deploy --from, with--previewfor a side-by-side current-vs-candidate overlay), saved-config CRUD, CloudWatch alarms CRUD, log streaming toggle, notifications endpoint, managed-update window, ALB scheme, instance type, key pair, IAM roles, deployment policy, rolling-update settings, health-check URL, capacity (:capacity— Min / Max / Instance type / Cooldown in one modal), plus a generic:set-option NAMESPACE OPTION VALUEescape hatch. - Worker / SQS workflow — DLQ viewer with per-message resend (
r), strict-typed purge (p), bulk delete (x), peek-and-tail with long-polling. - Multi-region / multi-account —
:region allfans across every configured region in parallel;:account NAMEswitches viasts:AssumeRole(configureaccounts.NAMEinconfig.toml);:accountslists AWS-Organizations child accounts;:find-envand:org-healthboth fan across~/.awsprofiles and configured AssumeRole accounts. - Bulk ops —
spacemulti-selects;:batch-rebuild,:batch-restart,:batch-deploy LABEL,:batch-tag KEY VALUE,:batch-untag KEY,:batch-set-option NAMESPACE OPTION VALUEall fan out in parallel with per-env audit + pending pill rows. - Safety —
--read-onlyCLI flag or:readonly ondisables every write surface; destructive actions (Terminate, DLQ purge) require typed-name confirmation; pre-flight dry-run shows impact (N instances across M AZs) + last 3 events before authorising; recent-change / mid-deploy traffic warnings in the confirm modal. - Audit log — every dispatched action and its outcome are appended to
~/.cache/ebman/audit.log; rotates at 1 MiB. - Power-user ergonomics — fuzzy command palette (
Ctrl-K) across commands / envs / saved views / plugins, named filters + saved views, custom keybindings (F1-F12and uppercase letters via~/.config/ebman/keys.toml), plugin commands (~/.config/ebman/commands.toml), in-app:loglevelreload,:diffbetween envs. - Headless / scriptable —
--control-socket PATHexposes a Unix-socket interface;ebman ctl <op>is a one-shot client (screen/state/key <spec>/cmd <:cmd>). - Non-interactive CLI —
ebman envs [--json]/ebman action rebuild --env NAME/ebman ctl ...for scripts and CI.
Install
Homebrew (macOS / Linux):
Pre-built binary:
Download the tarball for your platform from the GitHub Releases page, verify the *.sha256 next to it, extract, and put ebman on your PATH.
Cargo:
Tested on Rust 1.91+. macOS (Apple Silicon + Intel) and Linux x86_64. AWS SDK uses the standard credentials chain (AWS_PROFILE / AWS_REGION env, ~/.aws/credentials, instance role, etc.).
Fonts (optional, for the prettier glyph set)
Ebman runs fine in any terminal with the default icons = "unicode" config. For the Powerline-style pill chain, tab ribbon, and per-tab MDI icons (icons = "powerline" or icons = "auto"), your terminal needs a Nerd Font installed — vanilla Powerline fonts give you the triangles but tofu/boxes where the tab icons should be.
1. Install a Nerd Font:
2. Set your terminal's font to one of the Mono variants — they're sized for fixed-width TUIs (e.g. MesloLGS Nerd Font Mono, JetBrainsMono Nerd Font Mono):
- iTerm2: Preferences → Profiles → Text → Font
- Terminal.app: Preferences → Profiles → Font → Change
- Ghostty / Alacritty / WezTerm:
font-familyin the relevant config file - VS Code / Cursor terminal:
terminal.integrated.fontFamilyin settings
3. Tell ebman to use the new glyphs — either run :settings in ebman and pick auto (or powerline) from the Icons field, or add this to ~/.config/ebman/config.toml:
= "auto" # probes the terminal at startup; falls back to "unicode"
Restart ebman (or use ebman ctl reload if you're driving via the control socket) so the startup probe runs against your new font. icons = "powerline" skips the probe and forces the Nerd glyph set unconditionally.
Without a Nerd Font, stick to icons = "unicode" (the default) — everything still works, you just don't get the per-tab MDI icons.
Quickstart
Once running, press ? for a per-context keymap (Detail, DLQ, Action menu, Saved-configs overlay all have scoped help).
Keys
Normal mode (env table)
| Key | Action |
|---|---|
j / k / wheel |
Move selection |
g / G |
Top / bottom |
1 – 9 |
Jump to position |
' |
Name-jump (type prefix) |
Enter |
Drill into env |
Tab / Shift-Tab |
Switch scope (Envs ↔ Apps) |
a |
Actions menu |
b |
Open env in AWS console |
D |
Describe overlay (raw env JSON) |
space |
Multi-select |
* |
Pin / unpin |
! |
:why overlay (Red-env diagnostic) |
/ |
Filter |
: |
Command bar |
^K |
Command palette |
s / S |
Cycle sort key / reverse |
^G |
Toggle group-by-application |
^E |
Toggle events panel |
^] |
Cycle focus (table ↔ events panel) |
^D |
Cycle view mode (default / compact / spacious) |
^X |
Toggle redact mode |
^Y |
Yank filtered table as TSV |
^W |
Yank equivalent aws elasticbeanstalk describe-environments |
y / Y |
Yank CNAME / name |
f |
Freeze / unfreeze auto-refresh |
r / p |
Switch region / profile |
^R / F5 |
Force refresh |
? |
Help |
q / ^C |
Quit |
Detail view
Tabs cycle with Tab / Shift-Tab (or l / h). ^R re-fetches the active tab; R toggles per-tab auto-refresh; a opens the env actions menu; b opens the env in the AWS console.
| Tab | Per-tab keys |
|---|---|
| Health (default) | j/k walk items, Enter drill into source tab (Events / Instances / Queue) |
| Events | / filter, n/N next/prev match |
| Instances | Enter / i info overlay, b EC2 console, s embedded SSM shell, y yank id, x terminate (Y/N) |
| Metrics | [ / ] cycle range (15m → 24h), mouse hover for value-at-cursor |
| Queue | j/k Main ↔ DLQ, Enter opens viewer, d quick-open DLQ |
| Logs | ^R one-shot snapshot, s open live CW Logs streaming overlay, / filter |
| Config | scrollable; tags + env vars + cost estimate read-only |
DLQ viewer
j/k move, Enter view body, r resend (DLQ → main), x delete, p purge (typed-name confirm), m toggle Main ↔ DLQ, ^R re-peek.
Command reference
Type : to open the command bar. Tab-completion is not implemented, but Ctrl-K fuzzy-searches every command + env + view + plugin.
Navigation / inspection
:region NAME/:region all— switch region, or fan out across every configured region.:profile NAME— switch AWS profile.:account NAME— switch to a configured AssumeRole account (accounts.NAMEinconfig.toml). Falls back to:profile NAMEaliasing when noaccounts.entry exists.:accounts— list child accounts in the active AWS organization; rows matching a configuredaccounts.NAMEget a:account NAMEswitch hint.:sort KEY [desc]— set sort (name/app/status/health/version/age).:group on|off— toggle group-by-application.:redact on|off— toggle redact mode.:events on|off— toggle events panel.:filter NAME/:f NAME— load a saved filter.:save NAME/:drop NAME/:filters— manage named filters.:save-view NAME/:view NAME/:views/:view-drop NAME— saved views (filter + sort + grouping + scope).:cols list|hide NAME|show NAME|reset— manage columns.:pin— pin / unpin selected env.:alias NAME LABEL/:alias-drop NAME— local env aliases.:minimap on|off— corner mini-map of env health.:loglevel LEVEL— live-reload the tracing filter.
Per-env inspection
:why— Red-env diagnostic overlay (recent events / alarms / instance health / recent deploys; main + DLQ peek for Worker envs). Bound to!on the env table.:diff NAME— side-by-side env comparison.:resources/:res—DescribeEnvironmentResourcesdump.:alarms— CloudWatch alarms referencing the env.:versions— application versions (deployed marker, total count, deploy hint).:saved-configs/:configs— saved configuration templates (interactive:aapply,iinspect,xdelete,ccreate).:custom-platforms/:platforms— custom EB platforms.:plugins— list user plugin commands.:history— recent status / error log.:pending/:in-flight— overlay of dispatched actions + outcomes.:whatsnew— embedded changelog.:about/:credits— version, license, attributions.:update— show (and yank to clipboard) the upgrade command for whichever install channel (Homebrew / cargo-bin / tarball) ebman was installed from.:settings— interactive form to edit~/.config/ebman/config.toml; writes back on submit and live-applies theme / icons / refresh interval.
Write — env state
:rebuild/:restart/:terminate— action menu shortcuts (Terminate requires typed-name confirm).:deploy LABEL— ship an existing application version to the selected env.:deploy LABEL --preview— open a side-by-side overlay of the currently-deployed version vs the candidate (label, description, S3 source, timestamp + rollback / traffic warnings) without dispatching.:deploy --from PATH [--label L] [--describe D] [--no-deploy]— upload a local.zip(or--from s3://bucket/key), register a new version, optionally deploy.:upgrade [ARN]— list compatible platforms; with ARN, dispatch the migration.:clone NEWNAME— clone the selected env.:scale N/:stop/:start— set ASG min=max=N / 0 / 1.:capacity— modal form to edit Min / Max / Instance type / Cooldown in one shot (pre-filled fromDescribeConfigurationSettings).:swap TARGET— swap CNAMEs (Y/N confirm).:abort— abort an in-flight env update.
Write — env config
:env list | set KEY VAL | unset KEY— application env-var editor.:tag KEY VALUE/:untag KEY— env tag editor.:set-option NAMESPACE OPTION VALUE/:unset-option NAMESPACE OPTION— generic option-settings escape hatch.:instance-type TYPE— EC2 instance type (e.g.t3.medium).:keypair NAME/:service-role ARN/:instance-profile NAME— security tab.:public-ip on|off/:elb-scheme public|internal— network tab.:subnets/:elb-subnets/:security-groups— MultiSelect picker forms pre-filled with the env's current selection (lists available subnets / SGs from the VPC).:deployment-policy AllAtOnce|Rolling|RollingWithAdditionalBatch|Immutable|TrafficSplitting— deploy policy.:rolling-update on|off— ASG rolling-update policy.:health-check-url /path— HTTP health-check path.:logs-stream on|off [--retention DAYS]— toggle CW Logs streaming.:logs-tail [LOG_GROUP]— open a live streaming overlay for a CW Logs group (auto-picksweb.stdout.log).:notify EMAIL_OR_SNS_ARN | off— notification endpoint.:managed-window DAY HOUR | off— managed-platform-updates window.
Write — application versions / configs / alarms / platforms
:delete-version LABEL [--force]— delete an application version (with optional source-bundle removal).:config-save NAME/:config-apply NAME/:config-delete APP NAME/:config-inspect NAME— saved-configuration templates.:alarm-create NAME KIND THRESHOLD [OP]— CloudWatch alarm (KIND:health,4xx,5xx,latency).:alarm-delete NAME— remove a CW alarm.:custom-platform-delete ARN— delete a custom EB platform.:metric add LABEL NAMESPACE NAME [STAT] [DIM=VAL,...]/:metric remove LABEL/:metric list— custom Metrics-tab charts.
Multi-account / multi-region
:region all— fan acrossextra_regions+ current.:account NAME— switch to a configured AssumeRole account (see Configuration).:accounts— list AWS-Organizations child accounts; switch hints rendered for those with a configuredaccounts.NAME.:find-env SUBSTRING— scan every profile in~/.aws/{config,credentials}and every configured AssumeRole account in REGION.:org-health— aggregate env / red counts per profile + per configured AssumeRole account.
Multi-env
space— toggle multi-select.:batch-rebuild/:batch-restart— dispatch a non-destructive action across the selection.:batch-deploy LABEL— deploy the same version to every selected env in parallel.:batch-tag KEY VALUE/:batch-untag KEY— fan a tag write across the selection.:batch-set-option NAMESPACE OPTION VALUE— fan an option-settings write across the selection.:deselect/:select-clear— clear selection.
Output / yank
:export— yank filtered view as TSV.:json— yank filtered view as JSON array.:report/:markdown— yank filtered view as a Markdown table.
Read-only mode
:readonly on|off— toggle.--read-onlyon the CLI also locks every write surface.
Configuration
~/.config/ebman/config.toml:
# Refresh interval in seconds (default 15).
= 15
# Extra regions to expose in the region picker, comma-separated.
= ""
# Theme: "dark" (default), "light", or "high-contrast".
= "dark"
# Glyph set: "unicode" (default), "ascii" for low-feature terminals,
# "powerline" (alias "nerd") for Powerline-patched / Nerd Fonts, or
# "auto" to probe the terminal at startup and pick powerline if its
# support is detected (one-cell U+E0B0 advance), unicode otherwise.
= "unicode"
# Per-profile theme override — pin a theme per AWS profile so the screen
# itself says "you're in prod" without reading the breadcrumb. Format:
# "PROFILE:THEME,PROFILE:THEME". Theme names match the `theme = ...` key.
= "prod:high-contrast,staging:dark"
# Start with these toggles on (state.toml takes precedence after first run).
= false
= false
# Notification bell on increase in Red-env count.
= false
# Tag policy — flag envs missing any of these tags in the Config tab.
= "Owner,Project"
# Webhook URL to POST to when an env transitions to Red.
= ""
# AssumeRole targets reachable via `:account NAME`. One stanza per
# account. `source_profile` carries the base creds for the
# sts:AssumeRole call. `external_id` and `region` are optional.
# The temporary credentials build a fresh SdkConfig carrying only the
# assumed-role identity — source-profile creds never leak into request
# signing once the switch lands.
= "arn:aws:iam::111122223333:role/EbmanReadOnly"
= "default"
= "eu-west-2"
# accounts.prod.external_id = "..."
~/.config/ebman/keys.toml (optional) — custom keybindings:
# Aliases must be F1-F12 or an uppercase A-Z; map to a :command.
= "refresh"
= "region us-east-1"
= "history"
~/.config/ebman/commands.toml (optional) — user plugin commands. Each :NAME substitutes {name} / {cname} / {application} / {tier} / {region} / {profile} placeholders and yanks the rendered command to the clipboard.
[]
= "aws ssm start-session --target $(aws ec2 describe-instances --filters Name=tag:elasticbeanstalk:environment-name,Values={name} --query 'Reservations[].Instances[].InstanceId' --output text) --profile {profile}"
= "Yank a tunnel command into clipboard"
~/.config/ebman/state.toml is managed by the app — filter / sort / cursor position / named filters / saved views / pinned envs / custom metrics live there.
Headless interface (--control-socket)
Launch ebman with --control-socket PATH to expose a Unix-socket interface. A second binary, ebman ctl <op>, is the one-shot client (defaults to ~/.cache/ebman/control.sock).
Useful for integration tests, screenshot capture, scripted workflows.
What's stored locally
~/.config/ebman/config.toml— user configuration (see above).~/.config/ebman/keys.toml— optional custom keybindings.~/.config/ebman/commands.toml— optional plugin commands.~/.config/ebman/state.toml— persisted UI state: profile, region, filter, sort, grouping, redact, selected env, named filters, saved views, pinned envs, aliases, hidden columns, custom metrics. No credentials.~/.cache/ebman/ebman.log— application log; rotates as needed. SetRUST_LOG=debugfor verbose output.~/.cache/ebman/audit.log— every dispatched action and outcome (account, profile, region, action, target). Rotates at 1 MiB toaudit.log.1.~/.cache/ebman/crash-*.log— panic backtraces (10 most recent kept; 30-day TTL).- Clipboard —
y/Y/^Y/^Wwrite viaarboard.
Safety model
- Read-only mode (
--read-onlyor:readonly on) disables every write surface: action menu, DLQ resend / purge, all:-commands that mutate state. A greenREAD-ONLYpill in the header makes it visible. - Strict-typed confirm for irreversible actions: typing the env name is required to Terminate; typing the literal string to Purge.
- Pre-flight checks in the confirm modal:
DescribeInstancesHealthimpact count, last 3 events, traffic warnings for env-in-deploy / recently-changed / currently-Red. - Audit log records dispatch + outcome of every action.
Distribution
- Cargo:
cargo install ebmanfrom crates.io;cargo install --path .from a checkout. - GitHub Releases: tagging
v<X.Y.Z>triggers.github/workflows/release.yml, which builds release binaries forx86_64-unknown-linux-gnu,aarch64-apple-darwin, andx86_64-apple-darwinand attaches tarballs + SHA-256 checksums to a draft release. - Homebrew: tap lives at
tombaldwin/homebrew-tap. Per-release: bump the version + 3 platform SHAs inFormula/ebman.rbin both this repo (forbrew install --formula PATH) and the tap.
Development
See BACKLOG.md for in-flight and planned work. See CLAUDE.md for the AI-assisted-contributor rules (the project has been developed heavily with Claude Code).
License
Dual-licensed under MIT or Apache-2.0. See LICENSE-MIT and LICENSE-APACHE.