Lazytainer: stopping idle Docker containers until traffic arrives
Docker container lazy loading
At a glance
- What is it?
- Lazytainer watches packets on the ports you assign to a group and stops or pauses the containers in that group when nobody is talking to them. It is a small Go daemon that needs the Docker socket, a label on every container it manages, and a proxy port on itself.
- Who is it for?
- Adopt Lazytainer for self-hosted stacks with a few heavy services that sit idle most of the day, such as game servers or a whoami demo, where a short cold start is acceptable. Do not adopt it for anything that must answer instantly, for containers whose clients cannot tolerate a dead page on the first request, or for stacks where you cannot route traffic through the Lazytainer container.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 64 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The idle container problem Lazytainer targets
A self-hosted box often runs more services than it uses at any given moment. A Minecraft server, a Satisfactory server, a dashboard and a file indexer can all be up while only one of them is being touched. Each running container holds memory and, on a laptop or a small home server, keeps the CPU from reaching a low power state. Lazytainer addresses that gap: it monitors network traffic to a container and, when there is not enough of it, stops or pauses the container. When traffic returns, the container is started again. The README states the rule plainly: "If there is traffic, the container runs, otherwise the container is stopped/paused." The intended audience is people running Docker or Docker Compose on their own hardware who accept a cold start in exchange for a quieter machine. It is not a scheduler, not an autoscaler, and not a replacement for a reverse proxy. The repository topics include energy-efficiency, sustainability and greenit, which matches the framing: the point is wasted capacity, not orchestration.
How the traffic monitor and group model work
Lazytainer runs as a container of its own, built from a Go program in src/. The Dockerfile shows a two-stage build: a golang:1.23-alpine3.19 stage installs build-base, gcc, wget, git and libpcap-dev, compiles with CGO_ENABLED=1, and the runtime stage is a plain alpine image with libpcap-dev installed. The libpcap dependency is the tell for the mechanism: the daemon captures packets on a network interface, eth0 by default, and the netInterface group property exists to change that.
Containers are not managed individually. They are assigned to a group with a label, "lazytainer.group=<yourGroupName>", and the group itself is configured with labels on the Lazytainer container, each formatted as lazytainer.group.<yourGroupName>.<property>=value. The group properties documented in the README are ports (required, comma separated, and explicitly the internal port, not the exposed one), inactiveTimeout (seconds before the container is stopped, default 30), minPacketThreshold (minimum packet count for the container to stay on, default 30), ignoreActiveClients (base activity on packet count only, default false), pollRate (how often activity is checked, default 30 seconds), sleepMethod (stop or pause, default stop) and netInterface (default eth0).
The README is explicit that this is not automatic: "Lazytainer does not 'automatically' start and stop all of your containers. You must apply a label to them and proxy their traffic through the Lazytainer container." Traffic therefore has to reach Lazytainer's own published ports, which is why the example compose file attaches the managed containers with network_mode: service:lazytainer and leaves their own ports commented out. The daemon also needs /var/run/docker.sock mounted read-only, since starting and stopping containers means talking to the Docker API. The demo compose file sets pollRate=1 and inactiveTimeout=10 for both groups so the sleep and wake cycle is visible within seconds rather than minutes.
Installing Lazytainer and running the demo stack
The README's test path is to clone the repository and start the bundled compose stack. It produces two whoami containers reachable through the Lazytainer container.
git clone https://github.com/vmorganp/Lazytainer
cd Lazytainer
docker compose upThe README notes that if "docker compose" does not work you can try "docker-compose". After the stack is up, browsing to http://localhost:81 should show information about the container. Close the tab, wait for the logs to say "stopped container", and the same URL should serve a dead page. Hitting it several times generates enough traffic to bring the container back. Cleanup is a single command.
docker-compose downFor a real deployment you configure groups with labels on the Lazytainer service and attach each managed container to it. The repository's docker-compose.yaml shows the shape, including the required socket mount and the required ports property.
lazytainer:
image: ghcr.io/vmorganp/lazytainer:master
ports:
- 81:81
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
labels:
- "lazytainer.group.group1.ports=81"
- "lazytainer.group.group1.sleepMethod=pause"Managed containers carry the group label and share Lazytainer's network namespace, and the compose file comments that their own ports should not be configured because traffic is routed through Lazytainer. Two environment variables control logging and address families: VERBOSE=true for more verbose output, and IPV4_DISABLED=true or IPV6_DISABLED=true to switch off either family. If you would rather build the image yourself, the compose file comments say to comment out image:, uncomment build: ., and run DOCKER_BUILDKIT=1 docker-compose up --build.
Where Lazytainer breaks down
The first request after a sleep is the weak point. The README's own walkthrough expects that navigating to the port after the container has stopped gives "a dead page" until enough traffic arrives to wake it. Packet counting is not request counting, so a browser retry loop, a health checker or a monitoring probe can be what wakes a container, and a client that gives up after one failed connection will simply see an outage. Anything interactive with a strict timeout, such as a game client that drops on the first refused connection, is a poor fit for sleepMethod=stop.
pause is the cheaper option because the process keeps its memory, but that also means it keeps holding RAM, so the energy argument is weaker and the wake is faster. stop frees the memory but pays a full container start, which for something like a Minecraft server is measured in tens of seconds, not milliseconds. The README does not document rollback, migration between versions, or what happens to a container that is mid-write when the inactivity timeout fires, so any stateful service needs its own persistence story before you let Lazytainer stop it.
Operationally, the requirement that traffic be proxied through the Lazytainer container is the constraint that rules setups out. If your clients connect directly to a container's published port, or through a reverse proxy that targets the container rather than Lazytainer, the packet capture never sees the traffic and the container will be stopped underneath live users. The socket mount is also a genuine privilege consideration: the README requires /var/run/docker.sock, and read-only on the mount does not make the Docker API read-only for the process that holds it.
Lazytainer compared with a reverse proxy that wakes backends
The closest alternative in spirit is a reverse proxy that starts a backend on demand, for example a proxy configured to run a start command when a request arrives and then forward it. The difference is where the decision is made. A proxy sees HTTP requests, so it can wake on the first one and hold the connection until the backend answers, which removes the dead-page window that Lazytainer's packet counter produces. It also only knows about HTTP, and it needs a rule per service.
Lazytainer sits lower. It counts packets on an interface, so it is protocol agnostic: the examples directory covers minecraft, satisfactory and zerotier, none of which are plain HTTP request/response. It also works per group, so one group can wake several containers together, which is what the demo's group1 and group2 labels illustrate when traffic to whoami1 starts whoami2. The cost of that generality is the lack of request-level knowledge, which is exactly why the first connection can land on a stopped container. If all your services are HTTP behind one proxy, the proxy approach gives a better user experience; if you are running game servers and UDP daemons on a home box, Lazytainer's packet-level view is the one that generalises.
Maintenance, licence and upgrade cost
The repository is not archived, and its last push was on 2026-07-29. The most recent tagged release listed is v2.0.30 from 2025-03-19, preceded by v2.0.29 in February 2025 and v2.0.28 in September 2024. The gap between the last tag and the last push means master can carry changes that are not in a release, which matters if you pin the image: the repository's own compose file uses ghcr.io/vmorganp/lazytainer:master rather than a version tag. Pinning to master means your deployment follows the branch; pinning to v2.0.30 means you stay on the last tagged build until a new one appears.
Upgrading is a container image swap plus a restart, and the configuration surface is labels and environment variables, so there is no config file to migrate. That is cheap, but it also means a label typo fails quietly rather than loudly: the README does not describe validation output for malformed group labels. The project is MIT licensed, which permits commercial use and modification; the repository includes the LICENSE file at the top level. Note only that the Dockerfile installs libpcap-dev in the runtime image and the daemon needs the Docker socket, so the licence is not the thing to check with your security team, the mount is.
Editorial conclusion
Adopt Lazytainer for self-hosted stacks with a few heavy services that sit idle most of the day, such as game servers or a whoami demo, where a short cold start is acceptable. Do not adopt it for anything that must answer instantly, for containers whose clients cannot tolerate a dead page on the first request, or for stacks where you cannot route traffic through the Lazytainer container. Verify first that every managed container carries a lazytainer.group label, that the group ports are the internal ports rather than the exposed ones, and that /var/run/docker.sock is mounted read-only into the Lazytainer container.
Frequently asked questions
How does Lazytainer decide when to stop a Docker container?
It captures packets on a network interface and counts them per group. When activity stays below the configured threshold for longer than inactiveTimeout, the containers in that group are stopped or paused according to sleepMethod.
Does Lazytainer manage all of my containers automatically?
No. The README states that you must apply a lazytainer.group label to each container you want managed and proxy its traffic through the Lazytainer container.
What is the difference between sleepMethod stop and pause in Lazytainer?
stop shuts the container down, which frees its memory but requires a full start when traffic returns. pause keeps the process in memory, so it wakes faster but continues to hold RAM. The default is stop.
What volume does Lazytainer require?
The README says you MUST provide /var/run/docker.sock:/var/run/docker.sock:ro, because the daemon starts and stops containers through the Docker API.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/vmorganp-lazytainer)