Schedule icon
Request icon
Return icon
Switch icon
Log icon
SlackIncomingWebhook icon

Portainer Container Crash-Loop Sentinel

Monitor Portainer-managed Docker containers and alert Slack when one is stuck restarting or has exited.

Categories
Infrastructure

A container stuck in Docker's restart-backoff loop does not show up anywhere unless someone happens to run docker ps at the right moment. This blueprint authenticates against Portainer on a schedule, lists every container on a Docker endpoint through Portainer's own Docker API proxy, and pages Slack the moment one is crash-looping, has exited, or Portainer itself cannot be reached.

How it works

  1. login_to_portainer (io.kestra.plugin.core.http.Request) authenticates against POST /api/auth with a Portainer username and password, returning a jwt. allowFailure: true lets the flow continue into classification instead of aborting on a bad login.
  2. list_containers (io.kestra.plugin.core.http.Request) calls GET /api/endpoints/{endpoint_id}/docker/containers/json?all=true with the jwt as a Bearer token, returning Docker's own container list (every container's State: running, restarting, exited, paused, or dead).
  3. classify_container_health (io.kestra.plugin.core.debug.Return) checks whether list_containers.body is defined; if not, UNREACHABLE. Otherwise it counts containers with jq by State: any restarting container is CRITICAL (Docker is actively retrying a failing container), any exited container is AT_RISK, otherwise HEALTHY.
  4. route_by_container_health (io.kestra.plugin.core.flow.Switch) branches on that string: HEALTHY logs a quiet confirmation, AT_RISK and CRITICAL each page Slack with the specific container names, UNREACHABLE pages Slack separately, and defaults logs the raw outcome.
  5. log_audit_status records a status line on every run regardless of branch, so the execution history is a continuous container-health trend.
  6. The errors block pages Slack separately if the flow itself fails outright.
  7. The container_health_schedule trigger polls every 5 minutes with explicit input overrides, shipped disabled: true until you point it at a real Portainer instance.

What you get

  • A scheduled, endpoint-wide container crash-loop check with zero custom Docker Engine API client code.
  • A four-way status output (HEALTHY / AT_RISK / CRITICAL / UNREACHABLE) you can chart or gate other flows on.
  • A Slack page naming the exact container(s) behind a restart loop or an unexpected exit.
  • A separate failure channel for the sentinel flow itself, plus a standing audit log line per run.

Who it's for

  • Platform and DevOps teams running Docker workloads behind Portainer who want a container-level health check without wiring up a full metrics stack.
  • Small teams and homelab operators who use Portainer as their primary Docker management UI and want Slack paging on crash loops.

Why orchestrate this with Kestra

Checking docker ps by hand, or on a bare cron job, tells you nothing once the terminal closes. This flow keeps every login, every container snapshot, and every classification in the execution history, turns the health check into a typed output other flows can depend on, and gives you a Switch branch to grow into real remediation (restarting a specific container via the same Docker API proxy, opening a ticket, paging on-call) without rewriting the check itself.

Prerequisites

  • A Portainer Community Edition instance reachable at portainer_url. The free, official Docker image (portainer/portainer-ce) is enough to fully test this blueprint locally: docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer-ce:latest.
  • A Portainer user with API access and at least read permission on the target environment.
  • The numeric endpoint_id of the Docker environment to check, visible in the Portainer UI under Environments.
  • A Slack incoming webhook for the warning, critical, and unreachable alerts.

Secrets

  • PORTAINER_USERNAME: Portainer username used to authenticate against /api/auth.
  • PORTAINER_PASSWORD: password for that user.
  • SLACK_WEBHOOK_URL: Slack incoming webhook used for the AT_RISK alert, the CRITICAL alert, the unreachable alert, and the flow-failure alert.
  • OSS note: if your Kestra instance reads secrets from environment variables instead of a secrets backend, set SECRET_PORTAINER_USERNAME, SECRET_PORTAINER_PASSWORD, and SECRET_SLACK_WEBHOOK_URL as base64, for example echo -n "<your-value>" | base64.

Quick start

  1. Add the PORTAINER_USERNAME, PORTAINER_PASSWORD, and SLACK_WEBHOOK_URL secrets.
  2. Set portainer_url and endpoint_id to your Docker environment.
  3. Stop a test container (or point a bad image at a restart policy of always) so there is something to detect, run the flow once, and confirm status reads AT_RISK or CRITICAL.
  4. Enable container_health_schedule.

How to extend

  • Call POST /api/endpoints/{id}/docker/containers/{containerId}/restart as a safe remediation step for AT_RISK containers instead of only alerting.
  • Add a Loop task over list_containers to also surface each container's Image and uptime in the Slack message for faster triage.
  • Store each snapshot's counts in a KV store to build a longer crash-loop trend than the execution history alone provides.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.