|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: "Restart Unhealthy Docker Containers Automatically" |
| 4 | +date: 2025-07-018 -0500 |
| 5 | +category: "General" |
| 6 | +tags: [ "systemd", "high availablity", "setup" ] |
| 7 | +--- |
| 8 | + |
| 9 | +When using docker compose, I recently got into adding health checks for my containers. This helps a lot with startup, especially with dependednt containers, but I was under the impression if I had something like `restart: always` or `restart: unless-stopped`, it would automatically try and restart my container. That's just not the case so I looked for something that might. |
| 10 | + |
| 11 | +## The Watchtower Approach |
| 12 | + |
| 13 | +There is a container you can user to monitor the other running containers and restart them if they are unhealthy. It's called [autoheal by a dev named Will Farrell](https://github.com/willfarrell/docker-autoheal) (no relation?). The only issue is that running a container to make sure my other containers are running seems a little like asking the kids to watch each other, and also, and maybe more importantly, I don't know Will, and his project isn't marked official. Other than a bunch of stars, like most OSS projects, you just never know what you're running of who it's coming from. It's kind of the catch 22 of open source, but when giving something access to the docker socket, I just want to be careful, so I decided to do something different. |
| 14 | + |
| 15 | +## Stack Overflow |
| 16 | + |
| 17 | +The next bit of inspiration came from a [stack overflow post](https://stackoverflow.com/a/74014021/4686882). It's a bit down the page and not marked as "the answer" but it's like a gem in the ruff. |
| 18 | + |
| 19 | +```bash |
| 20 | +docker ps -q -f health=unhealthy | xargs --no-run-if-empty docker restart |
| 21 | +``` |
| 22 | + |
| 23 | +Between the answer and the comments, there it is, exactly what I needed. Basically get the unhealthy container IDs and pipe them into a docker restart. The answer recomended cron, but that would have been too easy. Instead I made a systemd unit. |
| 24 | + |
| 25 | +## Service and Timer |
| 26 | + |
| 27 | +Added to `/home/${USER}/.config/systemd/user`, I created my two systemd unit files. One is the server, and the second is a timer to trigger it. I also created a bash script file for the service to run. Three files for what could have been a single line in crontab, but hey, this is the _right way_, right? |
| 28 | + |
| 29 | +```bash |
| 30 | +mkdir -p /home/${USER}/.config/systemd/user |
| 31 | +cd /home/${USER}/.config/systemd/user |
| 32 | + |
| 33 | +cat <<EOF > restart-unhealthy.service |
| 34 | +[Unit] |
| 35 | +Description=Restart unhealthy docker containers |
| 36 | +After=docker.service |
| 37 | +Wants=docker.service |
| 38 | + |
| 39 | +[Service] |
| 40 | +Type=oneshot |
| 41 | +ExecStart=/home/${USER}/.config/systemd/user/restart-unhealthy.sh |
| 42 | +EOF |
| 43 | + |
| 44 | +cat <<EOF > restart-unhealthy.timer |
| 45 | +[Unit] |
| 46 | +Description=Run docker unhealthy restart every 5 minutes |
| 47 | +Requires=restart-unhealthy.service |
| 48 | +After=docker.service |
| 49 | +Wants=docker.service |
| 50 | + |
| 51 | +[Timer] |
| 52 | +OnCalendar=*:0/5 |
| 53 | +Persistent=true |
| 54 | + |
| 55 | +[Install] |
| 56 | +WantedBy=timers.target |
| 57 | +EOF |
| 58 | + |
| 59 | +cat <<EOF > restart-unhealthy.sh |
| 60 | +#!/bin/bash |
| 61 | +docker ps -q -f health=unhealthy | xargs --no-run-if-empty docker restart |
| 62 | +EOF |
| 63 | + |
| 64 | +chmod +x restart-unhealthy.sh |
| 65 | +``` |
| 66 | + |
| 67 | +Now we can register everything and start the service timer. |
| 68 | + |
| 69 | +```bash |
| 70 | +systemctl --user daemon-reload |
| 71 | +systemctl --user enable restart-unhealthy.timer |
| 72 | +systemctl --user start restart-unhealthy.timer |
| 73 | +``` |
| 74 | + |
| 75 | +## Script |
| 76 | + |
| 77 | +To make this easy, I created a gist that can be run from a script. |
| 78 | + |
| 79 | +> Don't just take my word for it. Always inspect the code that will be running on your machines, especially from an untrusted and unsigned source. |
| 80 | +{: .prompt-warning } |
| 81 | + |
| 82 | +```bash |
| 83 | +curl -s https://gist.githubusercontent.com/binarypatrick/d4faffc2807c1e68ddf1229acb057582/raw | bash |
| 84 | +``` |
0 commit comments