Dockge: Compose Stacks With a Sane UI
A web editor for compose files that leaves the compose files exactly where you left them

Contents
The reason I resisted container UIs for years is that most of them take custody of your
configuration. You paste a compose file into a text box, the tool stores it in its own
database, and from that moment the file on disk and the thing that is running have no
relationship. Your git log is a museum. The truth lives in a SQLite blob you cannot
grep.
Dockge is the tool that fixed that complaint, and it fixed it by having almost no opinion
at all. Every stack is a directory under /opt/stacks/<name> containing a compose.yaml
and a .env. Edit them in the web editor, and the file on disk changes. Edit them with
vim over SSH, and the web UI shows your changes. There is no import step and no export
step, because there is nothing to import from.
It comes from Louis Lam, who also wrote Uptime Kuma, and it shares that project’s sensibilities: small, focused, unfashionably practical, and slightly rough at the edges you rarely touch. If you already run Uptime Kuma as a status page, you will recognise the shape of the thing within about ten seconds.
What it is for
Dockge manages compose stacks. That is the whole scope. It will not build images. It has no concept of a Git repository, no templates, no app store, no blue-green deploys. What it gives you is five things:
A live editor for the compose file, with syntax highlighting and a form-based helper for
people who cannot remember port syntax. A start/stop/restart/update control per stack. A
streaming log view with all services interleaved, colour-coded by container. A converter
that takes a docker run command and turns it into compose YAML. And an agent mode that
lets one Dockge instance drive stacks on other machines.
That last one is the reason I keep it. The docker run converter is the reason everyone
else keeps it.
Setup
| |
The critical detail, and the one everybody gets wrong on the first attempt: the path in
DOCKGE_STACKS_DIR must be the same string on the host and inside the container. Dockge
shells out to docker compose using the socket, and the Docker daemon resolves paths in
the host’s filesystem namespace. If you mount /opt/stacks at /app/stacks inside the
container and tell Dockge to look there, it will read your compose files perfectly and
then hand /app/stacks/media to a daemon that has never heard of it. The error message
is unhelpful. Mount it at the same path both sides and the problem does not exist.
Then, on each additional host, run the identical stack and add it in the UI under Settings → Agents with its address and credentials. Dockge’s agents are full Dockge instances that happen to be driven remotely, which means if the central one dies you can still log into each machine directly. That is a nice property for a homelab.
Migrating in
There is no migration, which is the point. Move your existing directories:
| |
Refresh Dockge and both stacks are listed, running, with their logs available. It reads the directory. That is the entire onboarding process, and after Portainer’s import dialogs it feels like a trick.
Two constraints on naming. Dockge expects compose.yaml — it also accepts
docker-compose.yml, but the newer name is what it writes, and having half your stacks
on each is a small ongoing annoyance. And the directory name becomes the stack name and
therefore the compose project name, so renaming a directory orphans the running
containers. Rename by taking the stack down first.
The docker run converter, which everybody uses
Half the self-hosted software on the internet documents itself with a docker run command
of eleven lines and no compose example. Dockge has a text box: paste the command, get YAML.
| |
becomes a compose file with the right keys in the right places, which you then edit into the shape you actually want. It handles the common flags — ports, volumes, environment, restart policy, user, network mode — and produces something you would have typed yourself.
This is a five-minute time saving that you make twenty times a year, and it is the single
feature people mention when they recommend Dockge. It removes the specific friction that
makes you think “I’ll just run it with docker run for now”, which is how you end up with
a container nobody can account for and no file describing it.
Where it gets approximate: device mappings, --cap-add, --security-opt, cgroup parents
and healthcheck flags come out inconsistently, and anything with shell quoting inside it
needs a human. Read the output. It is a first draft that saves you the boring 90%.
Where it sits against the others
I compared Komodo and Portainer at length and concluded that the interesting axis is who owns the truth. Portainer owns it in its database. Komodo owns a definition of it in Git and reconciles. Dockge owns nothing — the filesystem owns it, and Dockge is a text editor with buttons.
That third position is stronger than it sounds, because it composes with everything else.
My stacks directory is a Git repo. I commit from the shell, Renovate opens PRs against it,
and Dockge happily edits the working tree without knowing any of that exists. When Dockge
writes a change, git diff shows it. When I want a stack back the way it was on Tuesday,
git checkout does it and the UI simply reflects the file.
No tool in this space is designed for that workflow. It falls out of Dockge having declined to build a data model. There is a version of this where that is laziness; in practice it is the most useful design decision in the project.
The trade is that everything the database was buying you is gone. No user permissions
beyond a single login. No audit of who changed what — git blame is your audit, if you
remembered to commit. No registry credential management. No overview of image versions
across hosts. I fill the last gap with
Diun watching the registries
and sending me a message, which costs 30 MB and works better than any dashboard.
The update button, and its sharp edge
Each stack has an Update button. It runs docker compose pull followed by up -d, which
is exactly what you would type. Convenient, and one click away from an unplanned upgrade
of something you meant to leave alone. It has no notion of a maintenance window, no
snapshot, no rollback beyond your own backups. I have pressed it on the wrong stack
exactly once — enough to make me pause over it now.
If the stack pins real versions rather than latest, that button does very little, which
is the correct amount. Pull on a pinned digest is a no-op. That behaviour is another
argument for pinning tags properly:
it turns a dangerous button into a harmless one.
Agent mode in practice
Four hosts, one browser tab. The stack list on the main instance shows everything across all four with the agent’s name beside each entry, and switching between them costs nothing. Logs stream from the remote host through the central instance over the same WebSocket, so a machine that is otherwise firewalled off from my desktop is still readable.
The rough edges are honest ones. An agent that goes offline — the cupboard laptop suspends itself if I forget to tell it otherwise — leaves its stacks in the list looking alive until you click one. Reconnection is automatic but unhurried. And there is no cross-host operation of any kind: you cannot move a stack from one agent to another, or run one action against all four. Each host is a separate tab in the same window.
For what it costs, that is fair. The alternative implementations of “see all four hosts at once” I have tried involved a database, an agent protocol and a fortnight of my life. This one involved adding a hostname and a password to a settings page.
Environment variables, secrets and the thing to watch
Each stack gets a .env alongside its compose file, and Dockge edits it in a second tab.
This is pleasant right up to the moment you realise what it means: your database passwords
are now one click away in a web UI, rendered in plain text, behind a single username and
password with no second factor.
Dockge has one local account and nothing else. No OIDC, no per-user scoping, no read-only
role for the person who just wants to look at logs. Whoever can reach port 5001 can read
every secret on four machines and start a container that mounts the host root. Any tool
holding the socket can do that, so the criticism lands on the architecture rather than on
Dockge. What Dockge adds is that the .env editor makes the secrets convenient, and
convenience is what turns a theoretical exposure into a habit.
My compromise: nothing sensitive lives in .env for the stacks that matter. Those read
from files:
| |
/opt/secrets sits outside the stacks directory, is mode 0600 and root-owned, and is
excluded from the Git repo. Dockge shows the compose file referencing a path and shows
nothing else. The UI stays useful and stops being a password vault.
The other half is access. Dockge listens on 5001 and should never be reachable from anywhere you have not decided it should be. Mine is bound to the loopback interface and reached through a reverse proxy that enforces the same authentication as everything else, so the single Dockge password is a formality behind a real login rather than the only door. If you take one thing from this section: do not put port 5001 on a LAN you share with a smart TV and consider the job done.
Troubleshooting
Stacks show as “inactive” though the containers are up. Dockge matches by compose
project name, which defaults to the directory name. Containers started with an explicit
-p project name, or with name: set inside the compose file to something else, will not
be associated. Either align the names or accept the stack living outside Dockge.
Editor saves, nothing happens. Check file ownership. Dockge runs as root in the
container and writes as root; if you have been editing as your own user with a
group-writable directory, the write can succeed while your shell edits silently conflict.
Pick one ownership model for /opt/stacks and stick to it.
Agent shows offline. The agent connection is a WebSocket to port 5001 on the remote host. Any reverse proxy in front of it needs WebSocket upgrade headers passed through, which is the default in some proxies and a config line in others. Test by hitting the agent’s UI directly first; if that works and the agent link does not, it is the proxy.
Logs stop streaming after a while. Same cause. An idle-timeout on a proxy in the path kills the socket and Dockge does not always reconnect visibly. Raise the read timeout.
docker run converter produces something that will not start. It handles common flags
well and exotic ones poorly — device mappings, cgroup parents and some network modes come
out approximate. Treat the output as a first draft. It saves you the boring 90%.
Is it worth it?
Dockge is worth it if the sentence “I want a web UI but I refuse to give up my compose files” describes you. That was precisely my position, and I have run it for months without a complaint that mattered.
It suits a homelab in the two-to-five-host range where you are the only administrator and your stacks already live in directories you back up. It is a poor fit if you need multi-user access control, if you want image builds, or if you have thirty stacks and need to see across them — the UI lists stacks, and past about fifteen the list is a list.
The real competition for that slot is a terminal and a bit of muscle memory. I still do
most of my work over SSH, because docker compose logs -f is faster to
type than the UI is to load. What Dockge earns is the evening use: something has fallen
over, I am on the sofa with a tablet, and I want to read the logs and restart one service
without finding a laptop. For that, a fast page that shows every stack across four hosts
and streams logs on tap is worth every one of its 90 MB.
Install it, point it at your existing directories, and if you dislike it, delete the container. Your stacks will not notice it has gone. That is a rare thing to be able to say about a management tool, and it is most of the recommendation.




