Files
homelab/knecht
2026-04-04 15:05:08 +02:00
..
2026-04-04 15:05:08 +02:00
2026-04-04 15:05:08 +02:00
2026-04-04 14:34:22 +02:00
2026-04-04 14:37:53 +02:00
2026-04-04 14:34:22 +02:00
2026-04-04 15:05:08 +02:00
2026-04-04 14:34:22 +02:00
2026-04-04 14:34:22 +02:00
2026-04-04 14:34:22 +02:00
2026-04-04 15:05:08 +02:00
2026-04-04 15:05:08 +02:00

knecht

CLI + TUI for managing Portainer stacks from the local services/ directory.

Installation

cd knecht
go build -o knecht .
# move to somewhere on your PATH, e.g.:
mv knecht /usr/local/bin/knecht

Configuration

~/.config/knecht/config.toml:

url    = "https://portainer.example.com"
token  = "ptr_..."
ignore = ["portainer"]   # stack names to hide (e.g. self-managed containers)

# Optional
endpoint     = "local"   # Portainer environment name, auto-detected if omitted
services_path = "/path/to/services"  # defaults to git root / services

The API token is generated in Portainer under Account → Access tokens.

Commands

Command Description
knecht Launch the TUI
knecht list Print all stacks with status and drift summary
knecht deploy <stack> Deploy a new stack from services/<stack>/docker-compose.yml
knecht update <stack> Update an existing stack, preserving Portainer env vars
knecht diff <stack> Print compose and env key drift
knecht logs [container] Open Dozzle in the browser; deep-links to a specific container if given

<stack> always maps to a folder name under services/.

TUI

Run knecht with no arguments to launch the interactive TUI.

┌─ stacks ──────────────┐ ┌─ detail ──────────────────────────────┐
│ ● traefik      live   │ │ rrr                                   │
│ ● rrr          live ~ │ │                                       │
│ ● jellyfin     live   │ │ status:  running                      │
│   dummy    not deploy │ │ drift:   compose ~3 lines             │
│                       │ │                                       │
│                       │ │ [u] update  [d] diff  [r] refresh     │
└───────────────────────┘ └──────────────────────────────────────┘

Keybinds

Key Action
/ Navigate stacks
u Update selected stack (preserves Portainer env vars)
D Deploy selected stack (only available when not deployed)
d Toggle diff view
e Generate or append .env.example from Portainer env keys
r Refresh all stacks
esc Return to summary view
q / ctrl+c Quit

Drift Detection

knecht compares the local services/<stack>/docker-compose.yml against what is deployed in Portainer, and the keys in services/<stack>/.env.example against the env vars set in Portainer.

Indicator Meaning
~ next to stack name Drift detected
! missing: KEY Key is in .env.example but not set in Portainer
? unknown: KEY Key is in Portainer but not in .env.example — press e to append
no .env.example Portainer has env vars but no example file exists — press e to generate

Stacks with no .env.example file are not penalised for env drift — absence of the file means "no opinion".

Stack Convention

Each stack is a directory under services/ containing:

services/<name>/
├── docker-compose.yml   # required
└── .env.example         # optional — documents required Portainer env vars

knecht deploy <name> / knecht update <name> read these files directly. Env var values are never stored locally — only the keys in .env.example, as documentation.