Skip to main content
Scale-to-Zero Docker Containers with Sablier and Traefik — Free Up RAM on Your VPS

Scale-to-Zero Docker Containers with Sablier and Traefik — Free Up RAM on Your VPS

Zarvelion Zynji
Author
Zarvelion Zynji
Tech enthusiasts (self-proclaimed). Gaming addict (diagnosed). Anime simp (no regrets). I turn my hyperfixations into content—welcome to the chaos.
Table of Contents

Your Docker Containers Are Eating RAM While Doing Nothing — Here’s the Fix
#

You’ve got 10, maybe 15 Docker containers running on your VPS. Maybe it’s a Raspberry Pi. Maybe it’s a mini PC tucked behind your router. Maybe it’s the cheapest VPS you could find. Either way, you know the pain: half those containers sit idle 90% of the time, yet they’re still chewing through precious RAM.

Uptime monitors pinging every 30 seconds. A dashboard you check twice a day. An internal tool nobody’s opened in a week. They’re all running. All consuming memory. All doing nothing useful.

What if those containers could sleep until someone actually needs them?

That’s exactly what Sablier does. It’s a free, open-source tool that integrates with Traefik to start containers on-demand and shut them down after a period of inactivity. Think of it as scale-to-zero for your self-hosted stack.

What Is Sablier?
#

Sablier is a lightweight middleware that manages the lifecycle of your Docker containers. When a request comes in for a sleeping container, Sablier wakes it up. When the container has been idle for a configurable amount of time, Sablier puts it back to sleep.

It works with:

  • Docker (standalone)
  • Docker Swarm
  • Kubernetes

And it integrates with popular reverse proxies including Traefik, Caddy, and Nginx.

The setup has two parts:

  1. Sablier server — the backend that talks to Docker via docker.sock to start/stop containers
  2. Sablier plugin for Traefik — middleware that intercepts incoming requests and coordinates with the server

Two Strategies: Dynamic vs Blocking
#

Sablier offers two strategies for handling the wake-up process. Choosing the right one depends on what your container serves.

Dynamic Strategy (Best for Web Apps)
#

The Dynamic strategy shows a loading page to the visitor while the container boots up in the background. Once the container is healthy, Sablier redirects to the actual app.

This works great for:

  • Web dashboards (Homepage, Dashy, etc.)
  • Internal tools with simple UIs
  • Any app where a 5-10 second loading screen is acceptable

Blocking Strategy (Best for APIs)
#

The Blocking strategy holds the incoming request until the container is fully ready, then forwards it. The visitor sees a brief delay but never sees a loading page.

Use this for:

  • REST APIs
  • Anything that can’t render an HTML loading page
  • Services where a clean response matters more than speed

Note for Safari/iOS users: The Dynamic strategy has known issues on Safari for iOS. If your users access your services from iPhones or iPads, use the Blocking strategy as a fallback.

Prerequisites
#

Before starting, make sure you have:

  • Docker and Docker Compose installed
  • Traefik v3.6.0 or newer (config via Docker labels is supported from this version)
  • A running Traefik instance with the Docker provider enabled
  • Basic familiarity with Docker labels and Traefik configuration

Step 1: Register the Sablier Plugin in Traefik
#

First, you need to tell Traefik about the Sablier plugin. Add it to your traefik.yml under the experimental section:

experimental:
  plugins:
    sablier:
      moduleName: "github.com/acouvreur/sablier"
      version: "v1.8.0"

If you’re running Traefik via Docker Compose, make sure this config is mounted correctly.

Step 2: Deploy the Sablier Server
#

The Sablier server needs access to docker.sock so it can start and stop containers. Here’s a minimal Docker Compose service:

services:
  sablier:
    image: acouvreur/sablier:latest
    container_name: sablier
    ports:
      - "10000:10000"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    command:
      - start
      - --provider.name=docker
    restart: unless-stopped

The server listens on port 10000 by default. Traefik will communicate with it there.

Pro Tip: Start All Containers in Sleep Mode
#

If you want Sablier to manage all your containers from the start (rather than them being running initially), use this flag:

command:
  - start
  - --provider.name=docker
  - --provider.auto-stop-on-startup

This tells Sablier to immediately stop any container it’s configured to manage, so everything starts in sleep mode.

Step 3: Configure the Sablier Middleware
#

Create a dynamic configuration file (or add it to your existing Traefik dynamic config) to define the Sablier middleware:

http:
  middlewares:
    sablier:
      plugin:
        sablierUrl: "http://sablier:10000"
        group: "my-apps"
        sessionDuration: "5m"
        theme: "hacker-terminal"

Here’s what each parameter does:

Parameter Description
sablierUrl URL of the Sablier server
group Docker label group that Sablier manages
sessionDuration How long a container stays alive after the last request (e.g., 5m, 30m, 1h)
theme Loading page theme for Dynamic strategy

Available Themes
#

Sablier ships with a few built-in themes for the Dynamic strategy loading page:

  • hacker-terminal — green-on-black terminal aesthetic
  • ghost — clean, minimal dark theme
  • shuffle — animated loading screen

Pick whichever fits your vibe. It’s a small detail, but it makes the loading experience feel intentional instead of broken.

Step 4: Add Labels to Your Target Containers
#

Now for the part that ties everything together. Add these labels to any Docker container you want Sablier to manage:

services:
  my-app:
    image: my-app:latest
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.my-app.rule=Host(`app.zynji.my.id`)"
      - "traefik.http.routers.my-app.middlewares=sablier@docker"
      - "sablier.enable=true"
      - "sablier.group=my-apps"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 10s

The key labels are:

  • sablier.enable=true — tells Sablier to manage this container
  • sablier.group=my-apps — must match the group name in your middleware config
  • traefik.http.routers.my-app.middlewares=sablier@docker — attaches the Sablier middleware to the router

Step 5: Add Health Checks
#

This is critical. Sablier needs to know when your container is actually ready to serve traffic. Without a health check, Sablier might redirect the user to a container that’s still initializing.

Add a healthcheck block to every container managed by Sablier:

healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
  interval: 10s
  timeout: 5s
  retries: 3
  start_period: 10s

Adjust the test command to match whatever health endpoint your app exposes. If your app doesn’t have a health endpoint, you can use a simple TCP check or a command that verifies the process is running.

Step 6: Test It Out
#

  1. Restart Traefik to load the plugin.
  2. Start your containers.
  3. Visit the app URL in your browser.
  4. You should see the loading page (Dynamic strategy) while the container boots.
  5. After the container is healthy, you’ll be redirected to the app.
  6. Wait for the sessionDuration to expire and check that the container stops.

You can verify the container stopped with:

docker ps --filter "name=my-app"

It should disappear from the running list after the session duration elapses.

Preventing Uptime Monitors from Waking Containers
#

Here’s a gotcha: if you use Uptime Kuma, UptimeRobot, or any HTTP monitor to check your services, those pings will wake up your sleeping containers — defeating the whole purpose.

Sablier solves this with the ignoreUserAgent parameter. Add it to your middleware config:

http:
  middlewares:
    sablier:
      plugin:
        sablierUrl: "http://sablier:10000"
        group: "my-apps"
        sessionDuration: "5m"
        theme: "hacker-terminal"
        ignoreUserAgent:
          - "curl"
          - "UptimeKuma"

Any request with a matching User-Agent string will pass through without waking the container. Your uptime monitor gets a response (from Sablier itself), but your app stays asleep.

When Scale‑to‑Zero Makes Sense (And When It Doesn’t)
#

This setup is ideal for:

  • Low‑RAM servers (Raspberry Pi, budget VPS with 1‑2GB RAM)
  • Internal tools that aren’t accessed constantly
  • Demo/staging environments that only need to be up when someone’s looking
  • Side projects where you want zero cost when nobody’s using them

It’s NOT ideal for:

  • Production services that need instant response times
  • Containers that take 30+ seconds to boot (the delay becomes noticeable)
  • Services with WebSocket connections that need to stay alive
  • Databases (don’t put your database behind Sablier — that’s a disaster waiting to happen)

FAQ
#

Does Sablier work with Docker Compose?
#

Yes. The label‑based configuration works directly with Docker‑Compose.

What happens if the Sablier server goes down?
#

Traefik will return an error for routes using the Sablier middleware; containers that are already running will continue to work.

Can I use different session durations for different containers?
#

The duration is set at the middleware level. To have different durations, create multiple middleware configurations with different sessionDuration values and assign them to different routers.

Is Sablier free?
#

Fully open‑source, no paid tiers.

How much RAM can I actually save?
#

If each idle container consumes 50‑100 MB, turning off 10 containers can save 500 MB‑1 GB. On a 2 GB RAM device, that’s a huge improvement.


Word count: ~1,450 | Reading time: ~6 min

Related


Load Comments