{"slug": "cooldeck-keyboard-first-terminal-ui-for-coolify", "title": "CoolDeck – keyboard-first terminal UI for Coolify", "summary": "CoolDeck, a keyboard-first terminal UI for the open-source Coolify deployment platform, launched with support for Linux, macOS, and Windows, enabling users to monitor deployments, logs, and instances without a browser. The tool, available via Homebrew, install scripts, and Go, includes a demo mode and direct REST API integration with OS keyring token storage.", "body_md": "**Your Coolify fleet, one keystroke away.**\n\nDeployments, logs, restarts and instance switching for [Coolify](https://coolify.io) -\nfrom the terminal you already have open.\n\n*No browser tab. No daemon. No token on screen. On purpose.*\n\n**English** · [Čeština](/Resetnak/cooldeck/blob/main/README.cs.md)\n\n[Quick Start](#-quick-start) · [Features](#-key-features) · [Fleet tail](#-fleet-tail) · [MCP](#-your-fleet-in-your-agent) · [Comparison](#-how-it-compares) · [Keyboard Shortcuts](#-keyboard-shortcuts) · [Installation](#-installation) · [Configuration](#-configuration) · [Security](#-security) · [Contributing](/Resetnak/cooldeck/blob/main/CONTRIBUTING.md)\n\n**Try the whole UI in one line - no Coolify instance, no token, no network:**\n\n```\n# macOS (Homebrew)\nbrew install resetnak/tap/cooldeck && cooldeck --demo\n\n# Linux & macOS (install script)\ncurl -fsSL https://raw.githubusercontent.com/Resetnak/cooldeck/main/install.sh | sh && cooldeck --demo\n```\n\nPrefer Go? `go install github.com/resetnak/cooldeck/cmd/cooldeck@latest`\n\n. Prefer a binary? Every\n[release](https://github.com/Resetnak/cooldeck/releases/latest) ships Linux, macOS and Windows\narchives plus `.deb`\n\n/`.rpm`\n\n/`.apk`\n\n. `--demo`\n\nruns against deterministic sample data - the same data\nthe golden tests render - so you can judge the product before you hand it a token.\n\nChecking whether a deploy went through should not cost you a browser tab, a login, and three clicks\nthrough a dashboard. **CoolDeck** puts the same fleet - statuses, deployment history, runtime logs and\nthe deploy button - into a terminal window you can leave open next to your editor, and drives all of it\nfrom the keyboard.\n\nIt talks to the Coolify REST API directly. No proxy, no agent, no daemon: one static binary that reads your config, pulls a token out of your OS keyring, and renders.\n\n```\n COOLDECK  Demo   ● connected   DEMO                              12 apps  |  refreshed just now\n────────────────────────────────────────────────────────────────────────────────────────────────\n RESOURCES        │ STATUS       APPLICATION       BRANCH         DEPLOYED  DOMAIN      │ billing-api\n                  │ ▲ Degraded   billing-api       main            47m ago  api.billi…  │ ▲ Degraded\n > Applications 12│ ◐ Restarting ingest-dashboard  feat/charts  1m 30s ago  -           │\n   Deployments  33│ ● Running    landing-web       main                21s  landing.e…  │ Branch   main\n   Instances     1│ ○ Queued     vault-web         main                 4s  vault.exa…  │ Commit   9f31c2e\n   Diagnostics    │ ■ Stopped    billing-worker    main             2h ago  -           │ Deploy   47m ago\n                  │ ● Running    shipyard-api      main             3m ago  shipyard.…  │\n────────────────────────────────────────────────────────────────────────────────────────────────\n  ↑↓ navigate   enter details   d deploy   r restart   s start/stop   / filter   ? more     1/12\n```\n\n**Look before you connect**:`cooldeck --demo`\n\n- the full UI on deterministic fake data, offline.**Create a Coolify API token**: in Coolify,*profile → API tokens*. Give it the least privilege you can live with (`read`\n\nplus only the write scopes you actually want).**Connect**:`cooldeck setup`\n\nwalks you through URL, token and keyring storage.**Use it**:`cooldeck`\n\n. Press`?`\n\nfor the key map,`:`\n\nfor the command palette,`/`\n\nto filter.\n\nOne degraded app, start to finish: / to filter, enter for the detail, l for runtime logs, d to redeploy behind a confirmation, 2 a to watch it land in the active queue. Rendered from\n\n[cassette.tape](/Resetnak/cooldeck/blob/main/cassette.tape).\n\n**🖥️ The whole fleet on one screen**: status, branch, last deploy and domain for every application, with filtering (`/`\n\n) and sorting (`S`\n\n).**🔎 Detail without a context switch**: overview, deployment history, runtime logs and configuration as tabs on the same screen.**🛰️ Fleet tail**: mark applications with`space`\n\n, press`t`\n\n, and read their runtime logs interleaved in one buffer, each line named and coloured by the application it came from - the view Coolify's web UI cannot give you.**📜 Real log ergonomics**: follow, pause, wrap, in-buffer search with`n`\n\n/`N`\n\n, copy the match, clear the buffer,`+`\n\n/`-`\n\nto widen or narrow the line window.**🚀 Operations behind a confirmation**: deploy, force deploy, restart, start/stop - every destructive action asks first, and only one mutation runs at a time.**🛟 Honest about failure**: a refresh that fails keeps the last good data on screen behind a stale banner instead of blanking the list. See[When Coolify blinks](#-when-coolify-blinks).**🔀 Several instances, one session**: switch fleets with`3`\n\nwithout restarting; add, edit and delete local instance entries from inside the TUI.**⌨️ Keyboard-first, mouse-optional**: vi-flavoured bindings borrowed from`lazygit`\n\nand`k9s`\n\n, a command palette (`:`\n\n/`Ctrl+K`\n\n) for the day you forget one, and a`?`\n\noverlay that always shows the truth - every hint is generated from a single`KeyMap`\n\n.**🎨 Eight themes, responsive layout**: auto, dark, light, Dracula, Catppuccin, Nord, Gruvbox, Tokyo Night; three-pane at 150+ columns, single column when the window is small.**🔐 Tokens you never see**: OS keyring by default, and tokens are kept out of the UI, the logs, the toasts and the diagnostics export by construction.**🤖 An MCP server in the same binary**:`cooldeck mcp`\n\nhands your fleet to an agent - read-only until you say otherwise. See[Your fleet, in your agent](#-your-fleet-in-your-agent).**🧪 Offline demo mode**:`--demo`\n\nis a full implementation of the same service interface, which is also what the golden snapshot tests render.\n\n**The one view Coolify's web UI cannot give you.** Mark the applications you care about with\n`space`\n\n, press `t`\n\n, and their runtime logs arrive interleaved in a single buffer - every line named\nand coloured by the application it came from. One incident, one screen, instead of a browser tab per\nservice.\n\nThree services marked, one buffer: space to mark, t to tail, / to search across all of them, w to wrap. Rendered from\n\n[tail.tape](/Resetnak/cooldeck/blob/main/tail.tape).\n\n`f`\n\nfollows, `space`\n\npauses, `c`\n\ncopies the merged buffer, `esc`\n\ngoes back. Coolify serves runtime\nlogs as whole snapshots rather than a stream, so CoolDeck polls one request per marked application\nevery 4 seconds, staggers them, and merges the replies by timestamp - up to five applications at a\ntime, and it says so when it drops the rest. The concurrency model is\n[ADR 0007](/Resetnak/cooldeck/blob/main/docs/decisions/0007-fleet-tail-concurrency.md).\n\n`cooldeck mcp`\n\nspeaks the [Model Context Protocol](https://modelcontextprotocol.io) over stdin/stdout,\nso an agent can ask what is running, why a build failed, and what the logs say - through the same use\ncases the TUI uses. No second HTTP client, no separate token, no daemon.\n\nA real JSON-RPC session against --demo - handshake, tool discovery, two calls, then the opt-in. Rendered from mcp.tape.\n\n**It cannot touch your production by default.** The read-only surface is `list_applications`\n\n,\n`get_application`\n\n, `list_deployments`\n\n, `get_runtime_logs`\n\n, `get_deployment_logs`\n\nand\n`get_instance_info`\n\n. `--allow-mutations`\n\nadds `deploy_application`\n\n, `restart_application`\n\n,\n`start_application`\n\nand `stop_application`\n\n- and nothing behind them asks for confirmation, because an\nagent has no one to ask. Grant it deliberately, and prefer a token scoped to the instance you are\nwilling to let it operate.\n\n```\n// Point any MCP client at the binary you already have:\n{ \"mcpServers\": { \"cooldeck\": { \"command\": \"cooldeck\", \"args\": [\"mcp\"] } } }\n```\n\nTry it before you wire it up: `cooldeck mcp --demo`\n\nserves the same tools against the offline demo\nfleet, so you can watch an agent work without a Coolify instance in the loop.\n\n📖 ** Full guide: docs/mcp.md** - client setup, every tool and its arguments, what to\ndecide before granting mutations, and troubleshooting.\n\nCoolDeck is not a replacement for the Coolify web UI - it is the fast path for the handful of things you do twenty times a day.\n\n| Tool | Great at | Where CoolDeck differs |\n|---|---|---|\nCoolify web UI |\nEverything - creating resources, editing env vars, managing servers | CoolDeck is read-and-operate only, but gets you from \"is it up?\" to \"redeployed\" in a few keystrokes, with no tab switch |\n`curl` + `jq` |\nScripting, one-off queries | CoolDeck gives you the same API with statuses, history and logs in one live view, and refuses to let a typo trigger a production deploy without confirming |\nk9s / lazydocker |\nThe container layer underneath | CoolDeck speaks Coolify's model - applications, projects, environments, deployments - not raw containers |\n\nEverything it does is an ordinary Coolify API call, so nothing here locks you in or out of the web UI.\n\nDashboards that clear the screen the moment a request fails are worse than useless during an incident. A failed refresh in CoolDeck keeps the last good snapshot, flags it as stale, and tells you how old it is. When the instance comes back, the next refresh heals it - no restart, and you keep your place in the list.\n\nCommand palette, a simulated outage ( F2 in demo mode), and the recovery. Rendered from outage.tape.\n\nEvery API call is bounded by a 20-second timeout, every request kind is cancellable, and stale replies from a superseded request are dropped rather than rendered.\n\n| Shortcut | Action |\n|---|---|\n`j` / `k` or `↑` / `↓` |\nMove selection |\n`g` / `G` |\nFirst / last item |\n`Ctrl+D` / `Ctrl+U` |\nPage down / up |\n`Tab` / `Shift+Tab` |\nNext / previous pane |\n`Enter` / `Esc` |\nOpen / back |\n`1` `2` `3` `4` |\nApplications · Deployments · Instances · Diagnostics |\n`q` / `Ctrl+C` |\nBack or quit / force quit |\n\n| Shortcut | Action |\n|---|---|\n`d` / `D` |\nDeploy / force deploy (confirms) |\n`r` |\nRestart (confirms) |\n`s` |\nStart or stop (confirms) |\n`l` / `L` |\nRuntime logs / build log |\n`b` / `o` |\nOpen primary domain / repository in the browser |\n`c` |\nCopy application UUID |\n`Space` / `t` |\nMark for the fleet tail / open the fleet tail |\n`S` |\nCycle sort: status → name → last deploy |\n`R` |\nManual refresh |\n\n| Shortcut | Action |\n|---|---|\n`/` |\nFilter applications (`status:` , `branch:` , free text) - or search the log buffer |\n`:` / `Ctrl+K` |\nCommand palette; disabled commands show why |\n`?` |\nHelp overlay with the complete key map |\n`Ctrl+T` / `Ctrl+W` |\nCycle theme / toggle compact layout |\n\n| Shortcut | Action |\n|---|---|\n`Space` |\nPause / resume polling (in the applications list, `Space` marks for the fleet tail instead) |\n`f` / `w` |\nFollow tail / wrap long lines |\n`/` · `n` · `N` |\nSearch · next match · previous match |\n`c` |\nCopy the buffer, or the current match |\n`+` / `-` |\nMore / fewer lines fetched (this session) |\n`Ctrl+L` |\nClear the local buffer |\n\nFull map: press `?`\n\nin the app, or read [docs/keybindings.md](/Resetnak/cooldeck/blob/main/docs/keybindings.md).\n\n**No runtime dependencies.** Every option below leaves you with a single static binary; only building\nfrom source needs a toolchain (Go **1.26.6+**, no CGO).\n\n```\nbrew install resetnak/tap/cooldeck\ncooldeck --demo\n```\n\nUpgrades come with `brew upgrade`\n\nlike anything else. The tap publishes a cask, which Homebrew on\nLinux does not support - on Linux, use the install script below.\n\n```\ncurl -fsSL https://raw.githubusercontent.com/Resetnak/cooldeck/main/install.sh | sh\n```\n\nDetects your platform, **verifies the checksum**, and drops the binary in `~/.local/bin`\n\n. Override\nwith `COOLDECK_INSTALL_DIR`\n\n, or pin a version with `COOLDECK_VERSION=v0.2.1`\n\n. Read it first if you\nwould rather not pipe a script into a shell - [it is short](/Resetnak/cooldeck/blob/main/install.sh).\n\n```\n# Resolve the newest tag once, then pick your package manager:\nVER=$(curl -fsSLI -o /dev/null -w '%{url_effective}' \\\n  https://github.com/Resetnak/cooldeck/releases/latest | sed 's|.*/v||')\nBASE=https://github.com/Resetnak/cooldeck/releases/download/v$VER\n\n# Debian / Ubuntu\ncurl -fsSLO \"$BASE/cooldeck_${VER}_linux_amd64.deb\"\nsudo dpkg -i \"cooldeck_${VER}_linux_amd64.deb\"\n\n# Fedora / RHEL\nsudo rpm -i \"$BASE/cooldeck_${VER}_linux_amd64.rpm\"\n\n# Alpine\ncurl -fsSLO \"$BASE/cooldeck_${VER}_linux_amd64.apk\"\nsudo apk add --allow-untrusted \"cooldeck_${VER}_linux_amd64.apk\"\n```\n\n`.deb`\n\n, `.rpm`\n\nand `.apk`\n\nare built for `amd64`\n\nand `arm64`\n\non every release - swap `amd64`\n\nfor\n`arm64`\n\nabove if that is your machine.\n\nDownload an archive for your platform from [Releases](https://github.com/Resetnak/cooldeck/releases/latest),\nunpack it, and put `cooldeck`\n\non your `PATH`\n\n:\n\n```\ntar xzf cooldeck_*_Darwin_arm64.tar.gz     # or Linux_x86_64, Linux_arm64, Darwin_x86_64\nsudo mv cooldeck /usr/local/bin/\ncooldeck version\n```\n\nWindows ships as a `.zip`\n\n. Every release carries a `checksums.txt`\n\n; verify before you trust it:\n\n```\nshasum -a 256 -c checksums.txt --ignore-missing\n```\n\nArchives for Linux and macOS (`amd64`\n\n& `arm64`\n\n) and Windows (`amd64`\n\n) are built by\n[GoReleaser](/Resetnak/cooldeck/blob/main/.goreleaser.yaml) from `v*`\n\ntags.\n\n```\ngo install github.com/resetnak/cooldeck/cmd/cooldeck@latest\ngit clone https://github.com/Resetnak/cooldeck.git\ncd cooldeck\nmake build          # -> bin/cooldeck, with version/commit/date baked in\n./bin/cooldeck --demo\ninstall -m 0755 bin/cooldeck ~/.local/bin/cooldeck\n```\n\nCoolDeck reads one TOML file. Find it - and check it - with:\n\n```\ncooldeck config path\ncooldeck config validate\n```\n\n| OS | Default directory |\n|---|---|\nLinux |\n`$XDG_CONFIG_HOME/cooldeck` or `~/.config/cooldeck` |\nmacOS |\n`~/Library/Application Support/cooldeck` |\nWindows |\n`%AppData%\\cooldeck` |\n\n```\nversion = 1                          # schema version; a newer one is rejected, never guessed at\ndefault_instance = \"production\"\ntheme = \"auto\"                       # auto|dark|light|dracula|catppuccin|nord|gruvbox|tokyo-night\nrefresh_interval = \"10s\"             # dashboard poll (minimum 3s)\nlog_refresh_interval = \"2s\"\nlog_lines = 300                      # default log window (10–10000)\nconfirm_destructive_actions = true\nconfirm_deploy = false               # set true to confirm ordinary deploys too\n\n[ui]\nnerd_font = \"auto\"                   # auto|on|off\ncompact_mode = \"auto\"\nmouse = true\n\n[instances.production]\nname = \"Production\"\nurl = \"https://coolify.example.com\"\ntoken_source = \"keyring\"             # keyring|command|env|plaintext\ntoken_key = \"production\"\n```\n\n`COOLDECK_CONFIG_DIR`\n\nmoves the whole directory - handy for keeping experiments away from your real\nsetup. Full reference: [docs/configuration.md](/Resetnak/cooldeck/blob/main/docs/configuration.md).\n\nResolved in this order: `COOLDECK_TOKEN`\n\nin the environment always wins, otherwise the instance's\n`token_source`\n\ndecides.\n\n| Source | Behaviour |\n|---|---|\n`keyring` |\nOS keychain, written by `cooldeck setup` or `cooldeck auth add` - recommended |\n`command` |\nRuns an external command and reads the token from stdout (e.g. `[\"op\", \"read\", \"op://…\"]` ) |\n`env` |\nReads a named environment variable |\n`plaintext` |\nToken in the config file (mode `0600` ) - last resort |\n\n```\ncooldeck auth add production      # store a token in the keyring\ncooldeck auth status production   # is one there? (never prints it)\nCOOLDECK_TOKEN=… cooldeck         # one-shot, nothing written anywhere\ncooldeck                          the TUI dashboard\ncooldeck --demo                   offline demo data, no Coolify needed\ncooldeck --instance production    start on a specific instance\ncooldeck --theme catppuccin       theme override for this run\ncooldeck --debug                  structured debug log (redacted)\n\ncooldeck mcp                      serve the instance to an agent over MCP (read-only)\ncooldeck mcp --allow-mutations    ... and let it deploy, restart, start and stop\n\ncooldeck setup                    first-run wizard: URL, token, keyring\ncooldeck theme                    interactive theme picker with live preview\ncooldeck auth add|status|delete <instance>\ncooldeck config path|validate\ncooldeck version                  version, commit, build date\n```\n\n**Tokens stay out of sight**: never rendered in the UI, never written to logs, toasts or the diagnostics export -enforces the log side, and the diagnostics dump is secret-free by construction.`internal/logging/redact.go`\n\n**Log output is sanitised**: raw ANSI control sequences from a remote log stream cannot repaint your terminal.** Only**URLs are ever handed to the browser.`http`\n\n/`https`\n\n**Deleting an instance** removes the*local*config entry and its keyring item. It never touches anything in Coolify.**Permissions degrade gracefully**: Coolify has no permission-introspection endpoint, so CoolDeck assumes full capabilities and switches individual features off on a`403`\n\n- showing them disabled with a reason rather than hiding them.**What it does not protect you from**: the config file is written`0600`\n\nbut a`plaintext`\n\ntoken source still puts the token on disk;`insecure_skip_verify = true`\n\nreally does disable TLS verification for that instance; and an MCP client started with`--allow-mutations`\n\ncan deploy, restart, start and stop without a confirmation, because an agent has nobody to ask. All three are opt-in, and all three are worth a second thought.\n\nReporting a vulnerability: [SECURITY.md](/Resetnak/cooldeck/blob/main/SECURITY.md).\n\nLayered so that the same use cases back the TUI, a CLI subcommand and the MCP server:\n\n```\ncmd/cooldeck → internal/cli        cobra, flags, config, service construction\n             → internal/tui        Bubble Tea model + views (presentation only, no I/O)\n             → internal/mcpserver  MCP tools over stdio (no HTTP client, no TUI imports)\n             → internal/app        Service interface = the use cases\n               ├── app/demo        deterministic fake service (demo mode + golden tests)\n               └── coolify         HTTP client + DTO → domain mapping\n             → internal/domain, config, credentials, logging, platform, version\n```\n\nBuilt on [Bubble Tea / Charm v2](https://github.com/charmbracelet/bubbletea). Every TUI screen is\ncovered by golden snapshots rendered from the demo service, so a layout regression fails CI instead of\nshipping.\n\n| Doc | |\n|---|---|\n|\n\n[Decisions](/Resetnak/cooldeck/blob/main/docs/decisions)[Coolify API](/Resetnak/cooldeck/blob/main/docs/coolify-api.md)[MCP server](/Resetnak/cooldeck/blob/main/docs/mcp.md)[Configuration](/Resetnak/cooldeck/blob/main/docs/configuration.md)[Keybindings](/Resetnak/cooldeck/blob/main/docs/keybindings.md)[Troubleshooting](/Resetnak/cooldeck/blob/main/docs/troubleshooting.md)[Changelog](/Resetnak/cooldeck/blob/main/CHANGELOG.md)\n\n```\nmake check               # fmt-check + vet + lint + test + build - run this before every PR\nmake run                 # go run ./cmd/cooldeck --demo\nmake test-race\nmake test-update-golden  # refresh the TUI snapshots, then *read the diff*\nmake lint                # staticcheck + golangci-lint (same versions as CI)\nmake bench               # view rendering benchmarks\nmake vuln                # govulncheck\n```\n\nThe four GIFs in this README are generated, not hand-recorded: `vhs cassette.tape`\n\n, `vhs tail.tape`\n\n,\n`vhs mcp.tape`\n\nand `vhs outage.tape`\n\nrebuild the binary and re-record against `--demo`\n\n, so they\ncannot drift from the working tree. All of them run in a throwaway `COOLDECK_CONFIG_DIR`\n\nunder\n`/tmp`\n\nand touch nothing of yours.\n\n**Shipped:** applications dashboard, detail with logs, confirmed mutations, deployments queue,\ndeployment outcome notifications, multi-instance management, diagnostics, eight themes, demo mode,\nan MCP server on the same `app.Service`\n\n, golden tests, multi-OS CI.\n\n**Next:** richer in-TUI token sources beyond the keyring, optional read-only views for services,\ndatabases and servers, and a non-interactive CLI for scripts and CI.\n\nBug reports, feature requests and PRs are welcome - see [CONTRIBUTING.md](/Resetnak/cooldeck/blob/main/CONTRIBUTING.md) for the\nlocal setup, the golden-test workflow and what `make check`\n\nexpects before review. Participation is\ncovered by the [Code of Conduct](/Resetnak/cooldeck/blob/main/CODE_OF_CONDUCT.md).\n\n[MIT](/Resetnak/cooldeck/blob/main/LICENSE) © 2026 Alexandr Rešetňak", "url": "https://wpnews.pro/news/cooldeck-keyboard-first-terminal-ui-for-coolify", "canonical_source": "https://github.com/Resetnak/cooldeck", "published_at": "2026-09-02 08:36:33+00:00", "updated_at": "2026-09-02 08:53:17.358837+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools"], "entities": ["CoolDeck", "Coolify", "Resetnak"], "alternates": {"html": "https://wpnews.pro/news/cooldeck-keyboard-first-terminal-ui-for-coolify", "markdown": "https://wpnews.pro/news/cooldeck-keyboard-first-terminal-ui-for-coolify.md", "text": "https://wpnews.pro/news/cooldeck-keyboard-first-terminal-ui-for-coolify.txt", "jsonld": "https://wpnews.pro/news/cooldeck-keyboard-first-terminal-ui-for-coolify.jsonld"}}