tugtainer is a free, open source orchestration & scheduling project written in Python and released under MIT. It has 1,549 GitHub stars, 57 forks and 6 open issues, and was last pushed 3 days ago. On this registry it ranks #77 of 130 tracked projects in Orchestration & Scheduling, with 5 head-to-head comparisons available. It gained 12 stars over the last 30 tracked days.

What is tugtainer?

Tugtainer is a self-hosted Python application with a web UI that automates updates of Docker containers, built for homelab operators and small infrastructure teams who want per-container control over what gets updated instead of blanket auto-updating everything on a host.

What it is

Tugtainer runs as a container itself, mounts the Docker socket read-only, and gives you a dashboard for checking and applying image updates across one or more Docker hosts. It ships as two images, ghcr.io/quenary/tugtainer:1 for the primary instance and ghcr.io/quenary/tugtainer-agent:1 for remote hosts, and it is licensed under MIT. The UI includes authentication, and the README states plainly that the application is distributed as-is and is not recommended for use in a production environment.

The problem it solves is container update drift. Instead of logging into each host, pulling images by hand, and recreating containers, an operator gets a scheduled check, a notification, and an optional automatic update. Automatic updates are disabled by default, so only the containers explicitly marked for auto-update are touched. It also handles the adjacent chores that usually sit outside an update script: image pruning, linked container handling for compose and custom links, healthcheck monitoring with auto-restarts, and basic start, stop, inspect, and log access. The README positions it against doing this work manually or from a separate tool such as Portainer, which it names as the fallback for recreating the agent and socket-proxy containers that Tugtainer cannot update from within itself.

Key capabilities

  • Per-container configuration, so each container is set to check only or to auto-update, with intentional updates enabled only where needed.
  • Multi-host management through a separate agent, registered in the UI under Menu -> Hosts and authenticated with the AGENT_SECRET shared secret.
  • Network access controls for remote agents via AGENT_ALLOW_NETWORKS (for example 192.168.0.0/24) and AGENT_ALLOW_ENDPOINTS (for example 10.0.0.5:9413), with only the built-in 127.0.0.1:8001 endpoint allowed by default when AGENT_ENABLED=true.
  • Crontab scheduling for checks and updates, plus notifications to a wide range of services documented in docs/NOTIFICATIONS.md.
  • Healthcheck monitoring with auto-restarts and notifications, and automatic or manual image pruning.
  • Private registry support, custom labels, socket proxy support, and linked container handling for compose files and custom links.
  • Basic container control and detailed container info covering inspect and logs.

Who uses it and how

  • Single-host homelab operators, who run one container on port 9412 with a tugtainer_data volume and a read-only /var/run/docker.sock mount.
  • Multi-host setups, where a primary instance manages remote agents over HTTP, optionally fronted by a reverse proxy for HTTPS.
  • Compose-based deployments, using docker-compose.app.yml for the primary instance and docker-compose.agent.yml for agents.
  • Teams terminating TLS with a public CA such as Let's Encrypt, which set the host URL to https:// and leave SSL on, or with a private CA by pasting the PEM into the Custom CA field.
  • Operators who want healthcheck failures to trigger an automatic restart and a notification rather than a silent outage.

Getting started

Use docker-compose.app.yml, or run docker pull ghcr.io/quenary/tugtainer:1 and start the container on port 9412 with the tugtainer_data volume and the read-only Docker socket mount, setting a strong, unique AGENT_SECRET, which is required.

How it compares

The facts provided do not name a set of paid products this project replaces, so no licence, cost, or hosting contrast can be drawn. Among the tools the README does name, Portainer appears only as the recommended fallback for manually recreating the Tugtainer agent and socket-proxy containers, since Tugtainer cannot update those from within the app.

When to use it — and when not to

A self-hoster must operate Docker socket access, a persistent tugtainer_data volume, and, for remote hosts, separate agent containers plus a reverse proxy if HTTPS is wanted; the README also warns that the image healthcheck uses http://localhost:8001 and may fail when uvicorn serves only HTTPS. Avoid it for production workloads, which the README explicitly does not recommend, and avoid it if you need the agent and socket-proxy containers to be self-updating or if you cannot take regular backups of important data. The practical path is to enable check-only mode first, receive notifications, and widen to auto-update only for containers you accept losing.

project readme (upstream, from github) — read inline

Tugtainer is a self-hosted app for automating updates of your docker containers

Please be aware that the application is distributed as-is and is not recommended for use in a production environment.

And don't forget about regular backups of important data.

Automatic updates are disabled by default. You can enable only what you need.

Table of contents:

Main features:

  • Web UI with authentication
  • Multiple hosts support
  • Socket proxy support
  • Crontab scheduling
  • Notifications to a wide range of services
  • Per-container config (check only or auto-update)
  • Automatic/manual check and update
  • Healthcheck monitoring with auto-restarts and notifications
  • Automatic/manual image pruning
  • Linked containers support (compose and custom)
  • Private registries support
  • Basic container control (start, stop, etc.)
  • Container detailed info (inspect, logs)

Deploy:

  • Quick start

    Use docker-compose.app.yml or the following docker commands.

    # create volume
    docker volume create tugtainer_data
    
    # pull image
    docker pull ghcr.io/quenary/tugtainer:1
    
    # run container
    # AGENT_SECRET is required. Set a strong, unique shared secret.
    docker run -d -p 9412:80 \
        --name=tugtainer \
        --restart=unless-stopped \
        -e AGENT_SECRET="" \
        -v tugtainer_data:/tugtainer \
        -v /var/run/docker.sock:/var/run/docker.sock:ro \
        ghcr.io/quenary/tugtainer:1
    

[!IMPORTANT] Keep in mind that you cannot update an agent or a socket-proxy from within the app because they are used to communicate with the Docker CLI. Avoid including these containers in a docker-compose that contains other containers you want to update automatically, as this will result in an error during the update. To keep them updated, you can activate "check" only to receive notifications, and recreate them manually or from another tool, such as Portainer.

  • Remote hosts

    [!IMPORTANT] Agent host URLs that resolve to private or reserved networks are blocked by default (best-effort check; see the security policy). The agent client then connects only to the addresses that passed that check, without replacing the hostname (TLS / virtual hosts stay intact). If your remote agent is on a LAN or Docker network, allow it on the primary instance via AGENT_ALLOW_NETWORKS (e.g. 192.168.0.0/24) and/or AGENT_ALLOW_ENDPOINTS (e.g. 10.0.0.5:9413). See .env.example. By default, only the built-in agent endpoint 127.0.0.1:8001 is allowed when AGENT_ENABLED=true.

    To manage remote hosts from one UI, you have to deploy the Tugtainer Agent. To do so, you can use docker-compose.agent.yml or the following docker commands.

    After deploying the agent, in the UI follow Menu -> Hosts, and add it with the respective parameters. The Agent secret field should match the AGENT_SECRET you've provided for the agent container.

    Backend and agent use HTTP by default. You can put a reverse proxy in front for HTTPS.

    • Public CA (Let's Encrypt, etc.): set the host URL to https://…, leave SSL on, leave Custom CA empty.
    • Private CA or self-signed: paste the CA PEM (or the self-signed certificate) into Custom CA, keep SSL on. The certificate hostname/SAN must match the URL host.
    • TLS on the agent without a reverse proxy: mount cert/key into the agent container and pass --ssl-certfile / --ssl-keyfile via command (see docker-compose.agent.yml). Paste the corresponding CA in the host settings. The image healthcheck uses http://localhost:8001 and may fail if uvicorn serves only HTTPS.
    # pull image
    docker pull ghcr.io/quenary/tugtainer-agent:1
    
    # run container
    # AGENT_SECRET is required. Set a strong, unique shared secret.
    docker run -d -p 9413:8001 \
        --name=tugtainer-agent \
        --restart=unless-stopped \
        -e AGENT_SECRET="" \
        -v /var/run/docker.sock:/var/run/docker.sock:ro \
        ghcr.io/quenary/tugtainer-agent:1
    
  • Socket proxy

    You can use Tugtainer and Tugtainer Agent without mounting the Docker socket directly.

    docker-compose.app.yml and docker-compose.agent.yml use this approach by default.

    Manual setup:

    • Deploy socket-proxy e.g. https://hub.docker.com/r/linuxserver/socket-proxy
    • Enable the following environment variables based on the features you want to use:
      • Base (check feature): CONTAINERS, IMAGES, POST, INFO, PING
      • Update feature: NETWORKS
      • Logs feature: ALLOW_LOGS
      • Container controls (start/stop/restart): ALLOW_START, ALLOW_STOP, ALLOW_RESTARTS, ALLOW_PAUSE, ALLOW_UNPAUSE
    • Set the env var DOCKER_HOST="tcp://my-socket-proxy:port" on the Tugtainer(-agent) container(s);

Private registries

To use private registries, you have to mount docker config to Tugtainer or Tugtainer Agent, depending on where the container with the private image is located.

  • Create the config using one of the methods on the host machine
    • Log into the registry docker login
    • Manually
      {
        "auths": {
          "": {
            "auth": "base64 encoded 'username:password_or_token'"
          }
        }
      }
    
  • Mount the config to the Tugtainer (Agent) as a read-only volume -v $HOME/.docker/config.json:/root/.docker/config.json:ro or in a docker-compose file.
  • That's all you need to do, Docker CLI will take care of the rest.

Custom labels:

  • dev.quenary.tugtainer.protected=true

    This label indicates that the container cannot be stopped. This means that even if there is a new image for the container, it cannot be updated from the app. This label is primarily used for tugtainer itself and tugtainer-agent, as well as for socket-proxy in the provided docker-compose files.

  • dev.quenary.tugtainer.depends_on="my_postgres,my_redis"

    This label is an alternative to the docker compose label. It allows you to declare that a container depends on another container, even if they are not in the same compose project. List of container names, separated by commas.

Hooks:

You can configure shell commands to run inside a container at points of the update lifecycle: pre_update, post_update, pre_stop, pre_rollback, post_rollback. Each command runs as sh -c "" inside the target container via the agent.

Tugtainer has no built-in database/service-specific backup logic — writing the actual backup/notification commands (e.g. pg_dump) and managing where their output goes is entirely up to you.

This feature is off by default and requires two things to be true at once:

  • ALLOW_HOOKS=true on the Tugtainer backend (feature gate — hides the UI form and stops the backend from ever calling the agent's exec endpoint when false).
  • ALLOW_EXEC=true on the Tugtainer-Agent for the specific host you want to run hooks on (defense in depth — an agent that hasn't opted in refuses to execute commands even if asked).

Failure semantics:

  • A failing pre_update or pre_stop hook aborts that container's update — the container is left running as-is, same as any other pre-flight check failure.
  • post_update, pre_rollback and post_rollback hook failures are report-only (logged) and never block anything — by the time these run, either the update already succeeded or a rollback is already underway and must complete regardless.
  • pre_rollback runs while the failed container is still alive, right before Tugtainer stops it to roll back.

The hooks form is hidden in the UI for protected containers (see Custom labels), since protected containers are never updated by the app.

Auth

The app uses password authorization by default. The password is stored in a file in encrypted form.

Alternatively, you can use an OpenID Connect provider instead of a password.

Auth cookies are not domain-specific and not HTTPS-only. All of this can be configured using env variables.

API

The backend API is served under the /api base path.

  • Swagger UI: /api/docs
  • Redoc UI: /api/redoc

Public endpoints

  • GET /api/public/health
  • GET /api/public/version
  • GET /api/public/summary (requires ENABLE_PUBLIC_API=true)
  • GET /api/public/update_count (requires ENABLE_PUBLIC_API=true)
  • GET /api/public/is_update_available (requires ENABLE_PUBLIC_API=true)

Env:

Most environment variables are optional. AGENT_SECRET is required for backend-agent communication. See .env.example for a list of vars with descriptions.

Frequently asked questions

Is tugtainer free to use?

tugtainer is open source under the MIT licence. There is no licence fee and no seat count — you can self-host it or, where the project offers one, pay a vendor for a managed version instead.

What does tugtainer do?

An application for automated Docker container updates with a web UI

What is tugtainer written in?

tugtainer is primarily written in Python. Its source is publicly available at https://github.com/Quenary/tugtainer, and it has 1,549 GitHub stars.