About

Server software is designed to be highly available such that once a daemon is stated, it will load up all the necessary per-requisites to server connecting clients as soon as they connect to the daemon. The problem is that whilst a daemon is up and running, it might use CPU, RAM and it also might access the drive while tending to itself, for instance, due to threads carrying out internal maintenance tasks within the daemon software.

However, iff. a service employs a stateless protocol for its communication with clients and for the duration that no request is made to the server, the resources that the service uses when it is idle are wasted or unnecessarily compete against the resources of other services. Normally, this would not be impactful because one would assume that daemons just load the minimal necessary to serve clients, however software like Elasticsearch is known to reach up to 2GiB of RAM while idle that it will occupy constantly without releasing it to the system.

This page describes a mechanism built on top of traefik using sablier that acts as a 3rd-in-line proxy and measures the time between accesses while shutting down services that have not been accessed for a user-configurable timeout.

Requirements

The examples given here are based on SystemD along the lines of the complete Servarr stack designed by Wizardry and Steamworks but the examples also references authelia from the all-in-one Docker template. It is recommended to give those two projects a look, in particular, any architectural diagrams in order to understand how the system works.

Otherwise, the setup uses sablier: Scale to Zero and from its own page, the architecture looks something like the following image depicts it.

Note that the diagram is a little incomplete in that the reverse proxies that lead to sablier can actually lead in order to any number of middlewares and not exclusively to sablier. Using a stack of middlewares, it is possible to first send the request through to an authenticator like authelia and only then to sablier in order to ensure that the services are not started unless a user authenticates beforehand.

Case Scenario

For the setup a case-scenario will be used where three services, namely mongo, redis and overleaf will be suspended after some inactivity time. The idea here is that "Overleaf" is a LaTeX text-processor suite that will not end up being used all the time and will probably only be required for work. However, the three services can use up CPU, RAM and keep the hard-drive busy.

Just for orientation, an almost empty "Overleaf" instance consumes $800MiB$, the MongoDB required by Overleaf consumes $200MiB$ and Redis consumes $8MiB$ of RAM. At the same time, the CPU seems to be used by MongoDB a whole lot. The video illustrates the utilization for a few seconds captured from ctop. For a single user and given that all these services are not accessed at the moment at all, the performance penalty seem way too high and those containers could be suspended just like any other regular software that runs on a computer that is started on demand and then shut down when not needed.

Note that from the all-in-one project, some part of the architecture is preserved, namely the following parameters:

  • the network shared by traefik, sablier and authelia is entertainment

Traefik

Traefik needs to be configured with a dynamic configuration folder. The following paramters should be amended such that they are added to the command line that starts traefik with Docker:

/usr/bin/docker run --name=traefik \
...
  -l traefik.docker.network=entertainment \
  -v /mnt/docker/data/traefik/dynamic_config:/etc/traefik/dynamic_config \
  -l traefik.docker.network=entertainment \
  traefik:3.4 \
  --providers.file.directory=/etc/traefik/dynamic_config \
  --providers.file.watch=true \
...

where:

  • /mnt/docker/data/traefik/dynamic_config is a path on the host that must be created that will store traefik configurations that will be dynamically reloaded when changed

Within /mnt/docker/data/traefik/dynamic_config a new file is created, namely dynamic-config.yml with the following contents:

http:
  services:
    overleaf:
      loadBalancer:
        servers:
          - url: "http://overleaf:80"
    overleaf-mongo:
    overleaf-redis:

  routers:
    overleaf:
      rule: "Host(`overleaf.SDM.duckdns.org`)"
      service: overleaf
      middlewares:
        - SERVICE_GROUP@file

  middlewares:
    overleaf:
      plugin:
        sablier:
          sablierUrl: http://sablier:10000
          sessionDuration: 1h
          group: SERVICE_GROUP
          dynamic:
            displayName: Overleaf
            refreshFrequency: 60s
            showDetails: true
            theme: shuffle

where:

  • SDM is a subdomain registered with DuckDNS
  • SERVICE_GROUP is a name that binds the services overleaf-mongo, overleaf-redis and overleaf by the same label so they can be stopped and started together on demand

Sablier

Configuring sablier is actually easy, and a SystemD service file is established for the sablier service:

[Unit]
Description=sablier
After=docker.service registry.service traefik.service
StartLimitIntervalSec=0
 
[Service]
Restart=always
RestartSec=5s
ExecStartPre=/bin/sh -c '/usr/bin/docker network create entertainment || true'
ExecStartPre=/usr/bin/docker pull sablierapp/sablier:latest
ExecStart=/usr/bin/docker run --name=sablier \
  --rm \
  --interactive \
  --hostname sablier \
  --net=entertainment \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  -l traefik.enable=true \
  sablierapp/sablier:latest \
  start --provider.name=docker --logging.level=trace
ExecStop=/usr/bin/docker stop sablier
ExecStop=/usr/bin/docker rm -f sablier
TimeoutSec=300
Environment=DOCKER_CONFIG=/etc/docker
 
[Install]
WantedBy=multi-user.target

Services

The services are all configured with similar additional parameters leading to a skeleton SystemD along the lines of:

[Unit]
Description=SERVICE_NAME
After=docker.service traefik.service
StartLimitIntervalSec=0

[Service]
Restart=never
ExecStartPre=/bin/sh -c '/usr/bin/docker network create entertainment || true'
ExecStartPre=/usr/bin/docker pull SERVICE_IMAGE 
ExecStart=/usr/bin/docker run --name=overleaf-SERVICE_NAME \
  --net=entertainment \
  --hostname=overleaf-SERVICE_NAME \
  --interactive \
  ...
  -l sablier.enable=true \
  -l sablier.group=SERVICE_GROUP \
  ...
  SERVICE_IMAGE \
  redis-server --appendonly yes
ExecStop=/usr/bin/docker stop overleaf-SERVICE_NAME
ExecStop=/usr/bin/docker rm -f overleaf-SERVICE_NAME
TimeoutSec=300
Environment=DOCKER_CONFIG=/etc/docker

[Install]
WantedBy=multi-user.target

where:

  • SERVICE_IMAGE is the Docker image,
  • SERVICE_GROUP is the same service group from the /mnt/docker/data/traefik/dynamic_config/dynamic-config.yml file,
  • SERVICE_NAME is a descriptive name for the service; following the examples, this should be either mongo or redis, as in overleaf-mongo and overleaf-redis

Note that compared to the all-in-one template, the SystemD file does not pass the --rm parameter to Docker. Omitting --rm is intentional because sablier will stop the container and iff. the container was previously stated with --rm, then the container cannot be started again because the volume is removed from Docker. Along the same lines, the SystemD configuration Restart=always now becomes Restart=never otherwise when sablier stops the container, the container will be restarted by SystemD.

Demonstration

The browser tab on the right is a ctop display and the dormant services that are turned off can be observed at the bottom of the list as "overleaf-mongo", "overleaf-redis" and "overleaf". The browser on the left starts to load the public URL to the Overleaf docker container (not mongo or redis) and sablier starts the "overleaf-mongo", "overleaf-redis" and "overleaf" containers in the background while presenting a loading screen to the user. Once all Docker containers in the group have been started, sablier leads the user to the landing page of the main service which is Overleaf in this case and Overleaf asks for authentication.


docker/traefik/suspend_and_resume_idle_containers.txt · Last modified: by office

Wizardry and Steamworks

© 2025 Wizardry and Steamworks

Access website using Tor Access website using i2p Wizardry and Steamworks PGP Key


For the contact, copyright, license, warranty and privacy terms for the usage of this website please see the contact, license, privacy, copyright.