Automatically Start and Stop your Containers with Lazytainer
My NAS is beginning to hurt. Sure it's a few years old now, the disks only a little younger, but I can see that some processes take a little longer, some services run a little slower. My CPU idles at 60%, and no matter how much RAM I have, it's never enough.
A big draw on resources is Docker, or rather, the 90-odd containers I have running. Only a handful are in constant use, but I'm too lazy to start and stop the others when I need, preferring to keep them up all the time for when I do need them. Wouldn't it be nice if there was a way of having that happen say, automagically?
Lazytainer
The way Lazytainer works is that it checks for packets going to a particular port on your machine. If there are none, then it will stop or pause the container. If sent packets exceed a certain threshold, it will restart the container.
The way it does this is pretty straightforward:
- It needs access to the docker socket (lets it pause/start/stop other containers)
- The traffic for the target container is routed through Lazytainer (lets it track packet send)
As a result, we need to make sure any published ports for target containers are published in the Lazytainer compose, and that certain labels are setup in both the target container and Lazytainer itself.
This guide will walk you through it.
Prerequisites
To be honest if you're here I'm going to assume you know how to use Docker on your machine, but before we get started, you'll need the following:
- Docker and docker-compose installed on your machine
- At least one container already running
- The ability to SSH / use CLI/terminal on your machine, or use Portainer to spin up your stacks
Understanding the Lazytainer and target container docker-compose.yaml
In our example, I'm going to use Excalidraw as it's one of the simplest containers I know (and a great tool to use as well, just sayin').
Here it is:
services:
excalidraw:
container_name: excalidraw
image: docker.io/excalidraw/excalidraw:latest
ports:
- 6080:80
restart: unless-stoppedAnd here is a basic Lazytainer compose:
services:
lazytainer:
container_name: lazytainer
image: ghcr.io/vmorganp/lazytainer:master
environment:
- VERBOSE=false #set to false unless you want to track packets for debugging
ports:
- #as per the container you want to start or stop
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
labels:
#the interval in seconds between checking packets
- "lazytainer.group.group1.pollRate=1"
#how many seconds of inactivity before the target container is stopped or paused
- "lazytainer.group.group1.inactiveTimeout=60"
#how man packets should be received by the target container to prevent a pause or stop
- "lazytainer.group.group1.minPacketThreshold=15"
#the CONTAINER PORT of the target container
- "lazytainer.group.group1.ports=#change me"
#can be either "stop" or "pause". if left blank, it will default to stop
- "lazytainer.group.group1.sleepMethod=pause"We now need to put them together into a stack so that they play nicely together:
services:
excalidraw:
container_name: excalidraw
image: docker.io/excalidraw/excalidraw:latest
# ports:
# - 6080:80
restart: unless-stopped
labels:
- lazytainer.group=excali #must match the group name used in the lazytainer labels
network_mode: service:lazytainer
depends_on:
- lazytainer
lazytainer:
container_name: lazytainer
image: ghcr.io/vmorganp/lazytainer:master
environment:
- VERBOSE=false #set to false unless you want to track packets for debugging
ports:
- 6080:80 #as per the container you want to start or stop
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
labels:
#the interval in seconds between checking packets
- "lazytainer.group.excali.pollRate=1"
#how many seconds of inactivity before the target container is stopped or paused
- "lazytainer.group.excali.inactiveTimeout=30"
#how man packets should be received by the target container to prevent a pause or stop
- "lazytainer.group.excali.minPacketThreshold=10"
#the CONTAINER PORT of the target container
- "lazytainer.group.excali.ports=80#change me"
#can be either "stop" or "pause". if left blank, it will default to stop
- "lazytainer.group.excali.sleepMethod=pause"Note the changes in the different services above:
- Excalidraw has had
portscommented out so they won't be included in the creation of the containerlabelsadded so Lazytainer knows what group it's part ofnetwork_modeadded to route traffic through the Lazytainer servicedepends_onbecause now without Lazytainer up and running, you can't access exaclidraw
- Lazytainer has added:
- the Excalidraw ports to
ports - all labels have had
excaliadded aftergroup.which matches the group set in Excalidraw - the
lazytainer.group.excali.portsnow =80 to match the internal Excalidraw port
- the Excalidraw ports to
Creating the containers
I mean, I think you know how to do this, but just in case:
- create your
docker-compose.ymlfile - paste the above into it
- run
docker-compose up -d(or use Portainer or Container Manager on Synology etc. etc.)
To verify that Lazytainer is working as it should, go into the logs. The below shows a 60 second timeout on a PaperlessNGX container I have, followed by unpausing it ~40 seconds later after I accessed it.

If you want more info specifically about packets and tracking (because maybe you want to tweak the packet threshold) then simply change the VERBOSE variable to true and restart both containers.
Tracking more than one container
Each Lazytainer container can manage multiple groups, and multiple containers within a single group.
Consider the following:
services:
lazytainer:
container_name: lazytainer
image: ghcr.io/vmorganp/lazytainer:master
environment:
- VERBOSE=false #set to false unless you want to track packets for debugging
ports:
- 6080:80 #C1
- 3000:3000 #C2
- 1234:5678 #C3
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
labels:
- "lazytainer.group.group1.pollRate=1"
- "lazytainer.group.group1.inactiveTimeout=30"
- "lazytainer.group.group1.minPacketThreshold=50"
- "lazytainer.group.group1.ports=80,3000"
- "lazytainer.group.group1.sleepMethod=pause"
- "lazytainer.group.group2.pollRate=1"
- "lazytainer.group.group2.inactiveTimeout=60"
- "lazytainer.group.group2.minPacketThreshold=10"
- "lazytainer.group.group2.ports=5678"
- "lazytainer.group.group2.sleepMethod=stop"C1, 2 and 3 above are Container 1, 2 and 3.
- C1 and C2 are in group1
- Unless traffic for both combined containers exceeds 50 within any 30-second period, both containers will be paused
- When paused and once traffic exceeds a certain packet threshold, both containers will be restarted
- C3 is in group2
- Once traffic is less than 10 packets in a 60-second period, the container will be stopped, and then restarted once it exceeds the threshold
Lazytainer and proxying the docker socket
A while back I wrote a guide to proxying your docker socket so that it's more secure while still providing access to containers which need it (especially if they're net-facing). This process also works with Lazytainer, and is something I'd recommend if you have the time to set it up.
Lazytainer and reverse proxies
If you're using something like Synology's built in reverse proxy where you specify the route with http://nasIP:port, nothing changes for you and whatever you've previously set up should still work.
If however you're using something like SWAG or NPM which can route requests by specifying a container name, you'll need to point your routing at Lazytainer.
excalidraw) in the proto, as well as the internal container port 80. As Lazytainer is now publishing that port, specifying lazytainer:80 (I'm paraphrasing here) will now serve Excalidraw.Lazytainer in one compose, target container(s) in another
In some cases you may want one Lazytainer container completely standalone, and use it with services from other docker-compose.yml files. This can be done by changing the target containers' network_mode to container:lazytainer which works by using the container name. Using the service name only works if it is in the same docker-compose.yml.
Final Thoughts
Lazytainer has a place in my docker stack. It's not always as quick to spin something up as I'd like, and I'm not sure I would have everything running through it, but there are a few (like Excalidraw, and other non-database services I infrequently use) for which this makes sense.
Related Articles


