📦 kontainy
Every Docker and Podman setting, in one interface
Rival tools hide the settings that matter. kontainy shows all of them — explained, with the gotcha attached
Why • Educational • Install • Features • Settings • Diagnostics • Templates • Learn • Privileges • Quick Start • Build
🎯 Why kontainy exists
Run Docker and Podman on the same machine for a week and you will meet all of
this: docker context use reports success and changes nothing, containers
"disappear" after installing Docker Desktop, a memory limit is accepted and
silently ignored, a log file quietly fills the root disk, and podman pull nginx fails on a name that works everywhere else.
None of these produce an error message. That is the problem kontainy is built around.
kontainy does not trust the context system. It connects to every socket it finds, separately, and shows them all in one table with an Engine column. A container is never lost — you can see which engine holds it.
The docker CLI resolves its target through five layers, and the top one wins:
1. -H / --host flag
2. DOCKER_HOST environment variable
3. DOCKER_CONTEXT environment variable
4. ~/.docker/config.json → currentContext
5. unix:///var/run/docker.sock
If DOCKER_HOST is set, the context is ignored completely — which is why
docker context use can say "success" and do nothing at all. kontainy shows
this chain layer by layer and marks the winner.
🎓 Educational by Design
kontainy never hides the command it is running. Create a container, change a setting, apply a fix — the exact shell command appears in the command strip at the bottom of every page, ready to copy. Every command is also written to a persistent history you can export as a shell script.
The point is not convenience. The point is that you should be able to do the same thing without kontainy afterwards.
This runs through the whole application:
- Create Container — the preview grows as you tick boxes, syntax
highlighted, and the same definition renders as a
docker runcommand, a systemd Quadlet unit, or a compose service - Settings — every key shows its CLI equivalent, which file it lives in, and whether a restart is needed
- Diagnostics — every finding ends in a command, and says whether it runs in user scope or needs root
- Learn — every snippet is a command you can actually type, with a Copy button
- History & Log — every command this session, filterable, exportable
📦 Install
pip install kontainy
kontainy
🐧 On Linux, pip may refuse to install
Most current distributions mark the system Python as externally managed
(PEP 668), so a plain pip install stops with
error: externally-managed-environment. Two ways around it:
# Isolated — recommended, no system packages touched
pipx install kontainy
# Into the system Python — needs the override flag
sudo pip install kontainy --break-system-packages --no-cache-dir -U
The same flag applies when upgrading a system-wide install later on.
Or download the standalone binary — no Python required:
| Platform | File | Notes |
|---|---|---|
| 🐧 Linux | kontainy-x86_64.AppImage |
chmod +x then run — the fully supported target |
| 🪟 Windows | kontainy.exe |
Portable. Docker Desktop and podman machine only |
| 🍎 macOS | kontainy-macOS-arm64 |
Apple Silicon |
Linux is the first-class target. Rootless Podman, Quadlet, systemd units, subuid mapping and linger only exist there, and roughly half the diagnostic rules are Linux-specific. The Windows and macOS builds work against Docker Desktop and
podman machine, and disable what does not apply.
✨ Features
⚙️ The settings catalogue
This is what kontainy is for. Docker Desktop and Podman Desktop were designed
for ease of use, and hide most of the configuration surface as a result —
daemon.json gets a raw JSON box with no explanation, containers.conf gets
nothing at all.
| Docker Desktop | Podman Desktop | kontainy | |
|---|---|---|---|
| Start / stop containers | ✅ | ✅ | ✅ |
daemon.json editing |
raw JSON box | ✗ | structured, explained, validated |
containers.conf / storage.conf |
✗ | ✗ | full catalogue |
registries.conf, short-name-mode |
✗ | ✗ | full catalogue |
| Which file a value came from | ✗ | ✗ | override chain shown |
| Declared vs effective value | ✗ | ✗ | compared, mismatch flagged |
| All engines in one table | ✗ | ✗ | ✅ with Engine column |
| Where your terminal points | ✗ | ✗ | ✅ resolved chain |
| Gotcha note per setting | ✗ | ✗ | ✅ 66 of 152 |
152 settings — 56 Docker, 67 Podman, 29 shared — spread across ten surfaces:
| Surface | Keys | Covers |
|---|---|---|
| 🌐 Networking | 25 | address pools, bridge, MTU, DNS, iptables/nftables, netavark, pasta |
| 🔐 Security | 23 | capabilities, seccomp, AppArmor, SELinux, user namespaces |
| 📊 Resource limits | 23 | memory, CPU, PIDs, ulimits, block I/O, OOM |
| ⚙️ Engine / Daemon | 19 | runtimes, cgroup manager, live restore, events |
| 📦 Registry | 18 | mirrors, insecure registries, short-name mode, pull policy |
| 🧱 Container | 14 | restart policy, healthcheck, mounts, init, timezone |
| 💾 Storage | 13 | drivers, graphroot, overlay options, quotas |
| 🔧 systemd / Quadlet | 8 | .container unit keys, auto-update, linger |
| 📝 Logging | 6 | drivers and rotation |
| 🏗️ Build | 3 | BuildKit and cache garbage collection |
Every entry carries: the key, the file it lives in, its type and valid choices, the default, the CLI equivalent, whether a restart is needed, user or root scope, a risk level, a description — and for 66 of them, the gotcha: what breaks when the setting is misunderstood.
Listing a setting is easy. Writing down what happens when it is wrong is not, and no rival tool does it.
🔬 Diagnostics
The equivalent of a linter for your container setup. Every rule says three things: what was found, why it happens, and the command that fixes it.
19 rules, none of which need root to detect:
| Group | Rules | Examples |
|---|---|---|
| CTX context | 6 | DOCKER_HOST overriding the context · Desktop leftovers (credsStore) · CLI plugins shadowing the distribution's · the podman-docker shim |
| POD Podman / rootless | 4 | socket not enabled · linger off, so containers die at logout · missing subuid range · auto-update timer inactive |
| NET networking | 3 | Docker address pool clashing with the local network or VPN · rootless ports below 1024 · nftables without the iptables layer |
| DSK disk | 2 | unlimited json-file logs filling the root disk · BuildKit cache that image prune does not touch |
| RES resources | 1 | cgroupfs on rootless cgroup v2, where limits are silently ignored |
| SVC systemd | 1 | docker.socket restarting the daemon you just stopped |
| DKR Docker Desktop | 1 | /dev/kvm missing or not readable |
| PER permissions | 1 | socket permission denied, group membership not yet applied |
📦 Templates
You should not have to hunt for example code. Seventeen ready-made
definitions, each carrying the details people get wrong when copying from a
blog post: the named volume that keeps the data, the environment variable the
image will not start without, a health start period long enough for the service
to come up, and a capability set that is not simply --privileged.
| Category | Templates |
|---|---|
| Databases | PostgreSQL 16 · MariaDB 11 · Redis 7 · MongoDB 7 |
| Web | nginx · Caddy 2 · Traefik 3 |
| Tooling | MinIO · Gitea · Vaultwarden · n8n · Pi-hole |
| Monitoring | Grafana · Prometheus |
| Development | JupyterLab · code-server · Ollama |
Each renders three ways from the same definition:
📋 PostgreSQL, all three formats
podman run -d \
--name postgres \
--restart=unless-stopped \
-p 5432:5432 \
-v pgdata:/var/lib/postgresql/data \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=<CHANGE_ME> \
--cap-drop=ALL \
--cap-add=CHOWN --cap-add=DAC_OVERRIDE --cap-add=FOWNER \
--cap-add=SETGID --cap-add=SETUID \
--memory=1g \
--health-cmd='pg_isready -U postgres' \
--health-start-period=30s \
docker.io/library/postgres:16
[Unit]
Description=PostgreSQL 16
[Container]
Image=docker.io/library/postgres:16
PublishPort=5432:5432
Volume=pgdata:/var/lib/postgresql/data
Environment=POSTGRES_PASSWORD=CHANGE_ME
DropCapability=ALL
AddCapability=CHOWN
AutoUpdate=registry
[Service]
Restart=always
[Install]
WantedBy=default.target
services:
postgres:
image: docker.io/library/postgres:16
restart: unless-stopped
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
cap_drop:
- ALL
volumes:
pgdata:
Quadlet generation is the part no other GUI has. podman generate systemd
is deprecated; Quadlet replaced it, and nothing but a text editor writes those
units today.
🧱 Create Container
Rival tools give you image, name, ports and volumes. kontainy gives you the whole surface, across seven tabs, with a live command preview underneath:
| Tab | Covers |
|---|---|
| Basics | image, name, command, entrypoint, working dir, user, restart policy, environment, labels |
| Network | network mode, published ports, hostname, DNS, extra hosts |
| Storage | volumes and bind mounts with :ro :z :Z propagation, tmpfs, read-only root, shm size |
| Resources | memory, swap, CPUs, cpuset, shares, PID limit, ulimits, OOM score |
| Security | privileged, no-new-privileges, user namespace, seccomp, AppArmor, SELinux, 18 capability checkboxes |
| Health | command, interval, timeout, retries, start period |
| Advanced | init, TTY, log driver and options, sysctls, devices, pod, passthrough flags |
📚 Learn
kontainy teaches container management, not kontainy. 16 categories, target 204 topics, each with explanation, diagram, table and runnable snippet.
| Category | Topics | Covers |
|---|---|---|
| ⚡ Quick Start | 8 | first container, ports, volumes, cleanup |
| 📦 Container Internals | 14 | namespaces, cgroups v1/v2, capabilities, overlayfs, OCI specs |
| 🐳 Docker | 18 | architecture, run flags, contexts, daemon.json, BuildKit |
| 🦭 Podman | 18 | daemonless design, rootless, pods, containers.conf |
| ⚙️ systemd & Quadlet | 10 | units, linger, socket activation, .container files |
| ☸️ Kubernetes | 20 | pods, deployments, services, kubeconfig, probes, RBAC |
| 🖥️ KVM / QEMU / libvirt | 12 | domain XML, qcow2, virtio, snapshots, VFIO passthrough |
| 🧱 LXC / LXD / Incus | 10 | system containers, idmap, storage pools, clustering |
| 🌐 Networking | 14 | bridges, macvlan, DNS, nftables, MTU, subnet clashes |
| 💾 Storage | 12 | volumes, bind mounts, overlay2, quotas, SELinux labels |
| 🔐 Security | 14 | rootless, capabilities, seccomp, signing, scanning, SBOM |
| 🏗️ Images & Registries | 12 | manifests, digests, multi-arch, buildah, skopeo, mirrors |
| 🎼 Compose & Orchestration | 10 | compose schema, profiles, healthchecks |
| 🔍 Troubleshooting | 14 | lost containers, permissions, full disks, exit codes |
| 🚀 Performance | 10 | crun vs runc, overlay vs fuse, cache strategy |
| 🔄 Migration & Interop | 8 | Docker to Podman, the shim, Desktop leftovers, WSL2, CI |
Diagnostic rules link into Learn: the rule tells you what to do, the topic explains why.
🔐 Privilege model
kontainy never elevates privileges. No pkexec, no sudo, no runas.
| File | Scope | What kontainy does |
|---|---|---|
~/.config/containers/containers.conf |
👤 user | writes |
~/.config/containers/storage.conf |
👤 user | writes |
~/.config/containers/registries.conf |
👤 user | writes |
~/.config/containers/systemd/* (Quadlet) |
👤 user | writes |
~/.docker/config.json |
👤 user | writes |
/etc/docker/daemon.json |
🖥 root | read-only + copyable command |
/etc/containers/* |
🖥 root | read-only + copyable command |
/etc/subuid, /etc/subgid |
🖥 root | read-only + copyable command |
/usr/share/containers/* |
📦 distribution | read-only, shown in the override chain |
Writes take a .bak backup, land atomically through os.replace — a
half-written daemon.json stops the daemon from starting at all — and preserve
existing comments in TOML files, including the ones explaining the very setting
being changed.
Rootless Podman keeps its entire configuration under ~/.config, which is why
this model costs so little: 65 of the 152 settings are directly editable.
🚀 Quick Start
From PyPI
pip install kontainy
kontainy
From source
git clone https://github.com/bayramkotan/kontainy.git
cd kontainy
python -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/python main.py
Linux — system dependencies
PySide6 needs the XCB platform libraries. On a minimal install:
# Arch / CachyOS
sudo pacman -S --needed libxcb xcb-util-cursor xcb-util-keysyms \
xcb-util-wm xcb-util-image xcb-util-renderutil libxkbcommon-x11
# Debian / Ubuntu
sudo apt install libxcb-cursor0 libxcb-xinerama0 libxcb-icccm4 \
libxkbcommon-x11-0 libxcb-keysyms1 libxcb-image0 libxcb-render-util0
# Fedora
sudo dnf install xcb-util-cursor xcb-util-keysyms xcb-util-wm \
xcb-util-image xcb-util-renderutil libxkbcommon-x11
For Podman support, enable the API socket:
systemctl --user enable --now podman.socket
loginctl enable-linger $USER # so containers survive logout
CLI
kontainy answers three questions without opening a window:
kontainy --scan # the context chain, every engine, systemd unit states
kontainy --doctor # run every diagnostic rule and print the findings
kontainy --stats # catalogue, rule and Learn counts
$ kontainy --scan
=== Terminal target ===
>> DOCKER_HOST (environment) unix:///home/you/.docker/desktop/docker.sock
DOCKER_CONTEXT (environment) —
config.json → currentContext desktop-linux
built-in default unix:///var/run/docker.sock
EFFECTIVE: unix:///home/you/.docker/desktop/docker.sock
=== Engines found ===
* unix:///home/you/.docker/desktop/docker.sock docker 29.8.0 (rootful)
unix:///run/user/1000/podman/podman.sock podman 6.1.2 (rootless)
unix:///run/docker.sock docker 29.8.1 (rootful)
📸 Screenshots
🏗️ Build from source
Builds are made in CI, on each platform's own runner — there is no
cross-compilation. Pushing a v* tag runs the whole pipeline: tests, then
Windows, macOS ARM64 and Linux AppImage builds, then a GitHub release with a
categorised changelog, then PyPI.
python build.py # one file, windowed -> dist/kontainy[.exe]
python build.py --debug # one file, console
python build.py --onedir # a directory -> dist/kontainy/
Run the test suite — 245 tests in under four seconds, because PySide6 is stubbed and the suite covers decisions rather than widgets:
pip install pytest
python -m pytest -q
🌍 Translations
The interface is English. A translation layer covering eleven languages is planned; see the project roadmap.
📝 License
MIT — see LICENSE.
⭐ If kontainy helps you, consider giving it a star! ⭐
Release files for kontainy 0.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| kontainy-0.0.1.tar.gz | 123.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| kontainy-0.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 254.4 kB
Release files / kontainy-0.0.1.tar.gz
| Download URL | kontainy-0.0.1.tar.gz |
|---|---|
| Size | 123.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3bdcbe340b8698a84574b7666dcb92c736e361f08ceb9f4ce1ea7a5d0c11a5d2
|
|
BLAKE2b-256 checksum How to use checksums |
8ac9f1915c1a15d781d18a1ebdc8abb89b78ff1315af2b6d95a9ede4885accbf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Release files / kontainy-0.0.1-py3-none-any.whl
| Download URL | kontainy-0.0.1-py3-none-any.whl |
|---|---|
| Size | 130.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3cf41a1951c611d075d42a08d07ae3665105af3522e81cbcf735fe4b87d358d4
|
|
BLAKE2b-256 checksum How to use checksums |
1fd9691543296f526eec974c7fc18e6bed6849ca6e87cb67980ad520bd341dbd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|