Skip to main content

Configurable desktop launcher for Docker-based applications

Project description

docker-app-launcher

Configurable desktop launcher for Docker-based applications. One persistent window. It opens, shows progress, and never closes itself — no dialog chains.

CI PyPI Python License: MIT

🇩🇪 Deutsche Version

Quick Start

pip install docker-app-launcher

Python API (3 lines)

from docker_app_launcher import LauncherConfig, launch

launch(LauncherConfig(
    app_name="My App",
    container_name="my-app",
    default_port=8080,
))

CLI

docker-app-launcher --config launcher.json   # open the window
docker-app-launcher --version                 # print the launcher version and exit
docker-app-launcher --check                   # is Docker running?
docker-app-launcher --status                  # print state and exit
docker-app-launcher --install --port 9000     # build + start headless
docker-app-launcher --start                   # start the stopped app
docker-app-launcher --stop                    # stop the running app
docker-app-launcher --uninstall               # remove containers/images
docker-app-launcher --cleanup                 # remove stale leftovers
docker-app-launcher --open                    # open the app in the browser
docker-app-launcher --debug ...               # verbose logging to stdout + launcher-debug.log

launcher.json

Everything is configurable. Only app_name is required — the rest is derived (slug, container/image names, compose project, config dir) or defaulted.

{
  "app_name": "My App",
  "container_name": "my-app",
  "default_port": 8080,
  "compose_file": "docker-compose.prod.yml",
  "install_dir": "/opt/my-app",
  "health_check_path": "/api/health",
  "health_check_key": "status",
  "health_check_value": "ok",
  "repo_url": "https://github.com/owner/repo",
  "app_version": "0.4.0",
  "update_check_enabled": true,
  "internal_ports": { "nginx": 80 },
  "env_internal_port_keys": { "nginx": "NGINX_PORT" },
  "show_advanced_ports": true,
  "locale": "en"
}

internal_ports, env_internal_port_keys, and show_advanced_ports are optional expert fields — omit them and the launcher behaves exactly as before (single host port, no advanced panel).

Features

  • One persistent window — never closes itself; only the X closes it.
  • Docker check on startup — distinguishes not installed / running / stopped / no Docker.
  • Live build progress — the Docker build is streamed line-by-line into the window.
  • Configurable port — editable in the GUI and via --port, validated and persisted (to launcher.json and the .env next to the compose file, so the launcher and Compose can't disagree).
  • Live port changes — the port field stays editable while the app runs; "Apply port" validates, rewrites .env, and recreates the stack in seconds (no rebuild — only the published host port changed), then health-checks on the new port.
  • Advanced internal ports (experts) — optional internal_ports / env_internal_port_keys let you remap in-container ports (full 1–65535 range, e.g. nginx :80); a collapsed "Advanced settings" panel (gated by show_advanced_ports) applies them with an image rebuild + health check. Empty by default: no extra .env keys, no UI, no behaviour change.
  • Verified actions — install runs a health check; uninstall re-lists the containers to confirm they are gone.
  • Install manifest + startup cleanup — finds and offers to remove stale leftovers of older installs.
  • Verbose uninstall / cleanup — every step reported with a ✓ / ✗ result.
  • Single-instance guard — a PID-based lockfile refuses a second launch with an "already running" notice instead of opening a duplicate window.
  • Background update check — checks GitHub Releases (derived from repo_url) and notes in-window when a newer release exists. Opt-out via update_check_enabled; silent on any network error.
  • File logging — a rotated launcher.log plus a per-run install.log under the config dir, and a launcher-debug.log on --debug. Best-effort: an unwritable dir degrades gracefully rather than crashing.
  • Concurrency-safe UI — every button is disabled for the full duration of an action and the window is held on top, so a second action can't be launched in parallel.
  • Quiet on Windows — every Docker subprocess runs with CREATE_NO_WINDOW, so an install no longer flashes a swarm of console windows (no change on Linux/macOS).
  • PyInstaller-ready — a bundled spec template, hidden-imports list, and build-time version injection for shipping frozen single-file builds.
  • System tray + "Run in background" (optional) — pip install docker-app-launcher[tray]; while running, the window minimizes to the tray (a visible Run in the background button and the X both use it). When the tray can't dock it falls back to a taskbar-minimized window, so it never silently closes.
    • Linux note: the reliable tray uses pystray's AppIndicator backend, which needs gi (PyGObject) plus the AppIndicator typelib. The [tray] extra pip-installs PyGObject (Linux-only; needs libgirepository1.0-dev, libcairo2-dev, pkg-config to build), and you also need the typelib at runtime — on Ubuntu: sudo apt install gir1.2-ayatanaappindicator3-0.1. If you instead rely on the system python3-gi (apt), create the venv with --system-site-packages so gi is importable. Without any of this the launcher still works — it just minimizes to the taskbar. Run with --debug to see which backend was selected.
  • DE / EN i18n (YAML) — strings live in per-language YAML files (i18n/de.yaml, i18n/en.yaml) loaded at startup; add a language by dropping a <code>.yaml file beside them. German uses real UTF-8 umlauts. Per-app overrides via custom_strings.
  • Actions architecture — all business logic is GUI-free and unit-tested without a display.
  • CLI ↔ GUI parity — both call the exact same actions layer.

Custom Icons

Configure window and system tray icons:

launch(LauncherConfig(
    app_name="My App",
    icon_path="path/to/app-icon.png",         # Window icon
    tray_icon_path="path/to/tray-icon.png",   # Tray icon (optional, falls back to icon_path)
))
{
  "icon_path": "branding/my-app-icon.png",
  "tray_icon_path": "branding/my-app-tray.png"
}

If no icon is configured, a default icon with the app's initial letter is generated automatically.

Supported formats: PNG (recommended), ICO, BMP. Recommended size: 256x256 (window), 64x64 (tray).

Cleanup Configuration

Configure which paths are searched for stale artifacts:

launch(LauncherConfig(
    app_name="My App",
    container_name="my-app",
    legacy_names=["old-app-name", "prototype-v1"],
    cleanup_configs=[
        "~/.old-app-name",
        "~/.config/old-app-name",
        "~/.local/share/old-app-name",
    ],
    cleanup_search_paths=[
        "~/.config/",
        "~/.local/share/",
        "~/",
    ],
))
{
  "legacy_names": ["old-app-name"],
  "cleanup_configs": [
    "~/.old-app-name",
    "~/.config/old-app-name"
  ],
  "cleanup_search_paths": [
    "~/.config/",
    "~/.local/share/",
    "~/"
  ]
}
  • legacy_names: Previous project names to find stale containers/images/volumes.
  • cleanup_configs: Explicit config directories to offer for removal.
  • cleanup_search_paths: Base directories searched for legacy_names subdirectories (<base>/<name> and <base>/.<name>).
  • Active project volumes are automatically excluded from cleanup.
  • User-data volumes are unchecked by default (opt-in deletion).

Configuration Paths

All launcher state is stored under config_dir (default: ~/.{app_slug}/):

~/.my-app/
  launcher.json          # Port, settings, preferences
  .env                   # Docker Compose port variables
  install-manifest.json  # Installed containers, images, history
  launcher.log           # Persistent log (rotated, 5MB max)
  install.log            # Last install/rebuild log
  launcher.lock          # Single-instance lockfile

Override the config directory:

launch(LauncherConfig(
    config_dir="~/.custom-path/my-app",
))

Install Manifest

The launcher automatically maintains an install manifest at {config_dir}/install-manifest.json. This file tracks every artifact created during installation, enabling precise cleanup without guesswork.

{
  "installed_at": "2026-06-24T14:30:00Z",
  "updated_at": "2026-06-24T18:15:00Z",
  "app_name": "My App",
  "app_version": "1.95.0",
  "launcher_version": "0.5.0",
  "port": 8501,
  "compose_project": "my-app",
  "compose_file": "/home/user/my-app/docker-compose.prod.yml",
  "containers": [
    {"name": "my-app-frontend", "image": "my-app-frontend:latest"},
    {"name": "my-app-backend", "image": "my-app-backend:latest"}
  ],
  "images": [
    "my-app-frontend:latest",
    "my-app-backend:latest"
  ],
  "volumes": [
    "my-app-data"
  ],
  "install_history": [
    {"action": "install", "version": "1.94.0", "at": "2026-06-20T10:00:00Z"},
    {"action": "update", "version": "1.95.0", "at": "2026-06-24T14:30:00Z"}
  ]
}

The manifest is:

  • Written after every successful install or start (with rebuild).
  • Updated with version and timestamp on each start.
  • Appended to install_history for every install/update/uninstall.
  • Marked as uninstalled (not deleted) on deinstallation.

How cleanup uses the manifest

With a manifest, cleanup knows exactly which containers, images and volumes belong to the current or previous installation. Without a manifest (legacy installs), it falls back to pattern-matching against container_name and legacy_names.

Cleanup with manifest:    Precise — removes listed artifacts only
Cleanup without manifest: Pattern-based — searches by name patterns

This is why the manifest is created automatically and should not be deleted manually.

Architecture

Module Responsibility
config.py LauncherConfig dataclass — the single source of truth.
actions.py All business logic. No tkinter. Fully testable.
gui.py LauncherApp(tk.Tk) — a thin window over the actions.
tray.py Optional system tray (pystray + Pillow).
i18n.py DE/EN strings with custom-string overrides.
lockfile.py PID-based single-instance guard.
update_check.py Background GitHub Releases update check.
logging_setup.py Rotated file logging (launcher.log / install.log).
subprocess_utils.py Windows CREATE_NO_WINDOW kwargs for all subprocesses.
pyinstaller/ Spec template + helpers for frozen builds.
__main__.py CLI entry point + GUI router.

Configuration reference

See LauncherConfig for the full field list (app identity, network/health, Docker timeouts, paths, GUI, links, cleanup, tray, i18n, and lifecycle callbacks).

Development

poetry install --with dev --all-extras
make ci        # lint + format-check + typecheck + tests
make test      # tests with coverage
make fix       # auto-fix lint + format

Used by

License

MIT © Asterios Raptis

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

docker_app_launcher-0.6.0.tar.gz (63.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

docker_app_launcher-0.6.0-py3-none-any.whl (71.7 kB view details)

Uploaded Python 3

File details

Details for the file docker_app_launcher-0.6.0.tar.gz.

File metadata

  • Download URL: docker_app_launcher-0.6.0.tar.gz
  • Upload date:
  • Size: 63.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for docker_app_launcher-0.6.0.tar.gz
Algorithm Hash digest
SHA256 d906bc7052bb63371e3431fd62344b18fd29b1ee56ecd5e3a46264786863f247
MD5 7cb600843af613af44dfb11c823720af
BLAKE2b-256 f783948f01cd97824da74cb29c8fadab7d56891d92d00615b9cece16da7e5ce5

See more details on using hashes here.

File details

Details for the file docker_app_launcher-0.6.0-py3-none-any.whl.

File metadata

File hashes

Hashes for docker_app_launcher-0.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c2f8c4a0cb76c2923c4792ec80b49632774c0680d1d9fe38c24b93a9eff80561
MD5 98d6ccbee8db321d0e04e08d164c759f
BLAKE2b-256 5a09b112a0b90f8ac616f08e46131a3506568b813bb0b018be77694060e463c7

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page