Skip to content

Fraser-Isbester/tusk

Repository files navigation

CI Go Report Card

Tusk

A k9s-style terminal UI for real-time PostgreSQL monitoring and management. This is Not a database client, it's a tool for observing and acting on live database activity.

Queries view
Transaction detail

Features

  • Live views — Queries, transactions, sessions, locks, tables, indexes with 2s auto-refresh
  • Rules engine — Define policy rules in YAML using CEL expressions that evaluate against live database state
  • Violation tracking — Audit log of rule violations with timestamped event lifecycle (detected → action → cooldown → closed)
  • Auto Remediation — Automatically take actions on rule violations based on defined policies
  • Split-pane detail views — Query detail with formatted SQL, transaction detail with query history, lock detail with blocker/blocked side-by-side
  • Interactive Activity pane — Shows lock contention and rule violations per PID; Enter on a blocking PID navigates to its detail
  • Tab navigation — Arrow keys cycle views, Tab cycles panes within detail views
  • Column sorting — Shift+letter toggles sort on any column
  • Filtering/ to filter any view, persists visually until cleared with Esc

Install

From source:

go install github.com/fraser-isbester/tusk/cmd/tusk@latest

Pre-built binaries: download from GitHub Releases.

Usage

# Direct connection
tusk 'postgres://user:pass@localhost:5432/mydb'

# Using a profile
tusk -P production

First run: if no connection can be established — no config yet, an unknown profile (tusk -P nope), or an unreachable database — tusk drops into an interactive setup screen instead of erroring out. Enter a connection (or a tunnel, see below), and tusk validates it and optionally saves it as a profile so the next launch connects straight through.

Daemon

tuskd runs a profile's rules engine headlessly — the same rules, evaluation, and auto-remediation as the TUI, with no terminal UI. Use it to enforce rules continuously in the background (e.g. under systemd).

Install:

go install github.com/fraser-isbester/tusk/cmd/tuskd@latest

(tuskd is also included in the release archives alongside tusk.)

Run:

# Evaluate the profile's rules every 2s (default)
tuskd -P production

# Custom polling interval
tuskd -P production -i 5s

tuskd requires the selected profile to define at least one rule; it reads the same ~/.config/tusk/config.yaml as tusk, honors readonly/dry_run, and shuts down cleanly on SIGINT/SIGTERM.

systemd unit (/etc/systemd/system/tuskd.service):

[Unit]
Description=Tusk rules daemon
After=network.target

[Service]
ExecStart=/usr/local/bin/tuskd -P production
Restart=on-failure
User=tusk

[Install]
WantedBy=multi-user.target

Configuration

~/.config/tusk/config.yaml

default_profile: dev

profiles:
  dev:
    url: "postgres://postgres:postgres@localhost:5432/mydb?sslmode=disable"
    rules:
      - name: kill-idle-in-txn
        resource: transaction
        when: "state == 'idle in transaction' && xact_duration > duration('5m')"
        action: terminate
        cooldown: 5m
        dry_run: true

      - name: long-queries
        resource: query
        when: "state == 'active' && duration > duration('30s')"
        action: cancel
        cooldown: 1m
        dry_run: true

  production:
    host: db.example.com
    port: 5432
    user: monitor
    password: 'secret'
    database: prod
    readonly: true
    rules:
      - name: idle-in-txn-5m
        resource: transaction
        when: "state == 'idle in transaction' && xact_duration > duration('5m')"
        action: terminate
        cooldown: 5m
        dry_run: true

Rule fields

Field Description
resource query, transaction, or lock
when CEL expression evaluated against the resource fields
action terminate, cancel, or log
cooldown Minimum interval between action firings per PID
dry_run Record violations but don't execute the action
readonly Profile-level: forces all rules to dry-run

CEL expression fields

Query: pid, user, app, database, state, duration, wait_event_type, wait_event, query, blocked_by, query_id, route, controller, action_name, framework

Transaction: pid, user, app, database, state, xact_duration, query_duration, query, lock_count

Lock: blocked_pid, blocking_pid, blocked_user, blocking_user, blocked_app, blocking_app, lock_type, mode, wait_duration

Connecting to remote / VPC databases

Production and staging databases usually aren't reachable directly — they live inside a VPC. A profile's connect: block tells tusk how to reach them, and tusk establishes the tunnel itself (and tears it down on exit). This works for both tusk and the tuskd daemon.

Kubernetes port-forward (e.g. Postgres in a GKE cluster) — tusk runs kubectl port-forward for you:

profiles:
  staging:
    connect:
      via: kube-port-forward
      context: gke_myproject_us-central1_staging   # optional; omit for current context
      namespace: databases
      target: svc/postgres                         # svc/pod/deployment/statefulset
      remote_port: 5432
      # local_port: 0   # 0 = auto-pick a free port
    user: readonly
    database: appdb
    sslmode: disable

Arbitrary tunnel command (exec) — the escape hatch for SSH bastions, the Cloud SQL Auth Proxy, or anything else. {local_port} is substituted with an auto-picked free port that tusk then connects to:

profiles:
  bastion:
    connect:
      via: exec
      command: ["ssh", "-N", "-L", "{local_port}:db.internal:5432", "bastion.example.com"]
    user: readonly
    database: appdb
  cloudsql:
    connect:
      via: exec
      command: ["cloud-sql-proxy", "--port", "{local_port}", "myproj:us-central1:pg"]
    user: readonly
    database: appdb

For tunnel methods, credentials and the database name come from the profile fields (user/password/database/sslmode); host and port are supplied by the tunnel. The default method is direct, which uses url/fields as-is. kubectl / cloud-sql-proxy / ssh must be on your PATH.

Views

Command Description
:queries Active queries with duration, wait events, blocking info
:transactions Active transactions sorted by age with lock counts
:sessions Connections grouped by user, app, state
:tables Table sizes, row counts, dead tuple %, vacuum stats
:locks Blocked/blocking lock pairs
:indexes Index scan counts and sizes
:rules Configured rules with violation counts
:violations Violation audit log with event timeline

Key bindings

Key Action
Switch views
: Command prompt
/ Filter
Tab Cycle panes in detail view
Enter Drill into detail / fire action
Esc Back / clear filter
Shift+letter Sort by column
c Cancel query (in query detail)
t Terminate backend (in query/transaction detail)
n New rule (in rules view)
e Edit selected rule (in rules view)
d Delete selected rule (in rules view)
Space Toggle rule enabled/disabled (in rules view)
q Quit

Development

task db:up        # Start local Postgres in Docker
task run          # Build and run against local dev DB
task loadtest     # Generate realistic load (queries, transactions, locks, idle-in-txn)

Testing

task test         # Run all tests with Ginkgo
task test:cover   # Run tests with coverage report

About

Postgres admin TUI

Resources

License

Contributing

Stars

5 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors