From PM2 to Docker: Automatic Restarts
Tags: node, docker, webdev, devops
Published: 2020-04-19 09:52:44

*This article was originally published a day earlier at https://maximorlov.com/pm2-to-docker-automatic-restarts/*

Are you having trouble migrating from PM2 to Docker? Or do you want to know if Docker can do the same things as PM2?

In the *From PM2 to Docker* series, you will see how important features from PM2 translate to their Docker counterparts. You will learn how to deploy your application the Docker way, and boost your confidence in deploying to production environments.

By using Docker, you leverage a robust ecosystem that brings standardisation to deployments. Your application will be able to handle serious production heavy workloads.

This article covers automatic restarts. We'll go over all the restart policies offered by Docker and how they translate to a PM2 based configuration.

## Automatic restarts
PM2 and Docker have different default settings when it comes to keeping your application online. You get out-of-the-box automatic restarts with PM2, whereas with Docker you have to explicitly enable them.

### Restart policies
When you start your [containerised application](https://maximorlov.com/a-beginners-guide-to-building-a-docker-image-of-your-nodejs-application/), you can choose one of several restart policies provided by Docker. The syntax for Docker CLI is `--restart=<policy>` and in Docker Compose, the configuration is nested at the service level with `restart: <policy>`. There are examples for all possible restart policies at the bottom of this section.

Let's go through each restart policy.

- `no`  — Do *not* automatically restart the container when it exits. This restart policy is the default in Docker.

To achieve the same result in PM2, you would use the `--no-autorestart` flag in the command line or `autorestart: false` in your configuration file.

- `on-failure[:max-retries]` — *Only* restart the container if it exits with a non-zero code. Optionally, you can pass a limit to the number of restart attempts.

PM2 doesn't allow you to change the restart behaviour based on the exit code of your application. Whether your app has exited successfully or with an error, PM2 will treat them *equally*.

The maximum number of restart attempts in PM2 is configured with the `--max-restarts` flag or `max_restarts` option in the configuration file.

- `unless-stopped` — Always restart the container, *regardless* of the exit code. On Docker daemon startup (e.g. after a server restart), *only* start the container if it was already running before.

You cannot set a limit to the number of restart attempts with this policy (and with the `always` policy). Docker will try to restart your container indefinitely until you manually stop (`docker stop`) or remove the container (`docker rm -f`).

The first half of this policy is the default with PM2. For the second half, you have to use a combination of `pm2 startup` and `pm2 save` commands. Only then will your currently running applications persist after server restart. By default, PM2 does *not* restart your applications on server restart.

- `always` — Always restart the container, *regardless* of the exit code. The container will also start when the Docker daemon starts, *even* if it was in a stopped state before.

This policy is similar to `unless-stopped`, with the exception that your application container will restart even if it was (manually) stopped before the Docker daemon shut down.

Below are examples of all possible restart policies if you're using the Docker CLI to start your container:

```bash
# Do not automatically restart the container when it exits. This restart policy is the default. Therefore both commands are the same.
docker run app
docker run --restart=no app

# Only restart the container if it exits with a non-zero code.
docker run --restart=on-failure app

# Same as above, but limit the number of restart attempts to 10.
docker run --restart=on-failure:10 app

# Always restart the container, regardless of the exit code. On Docker daemon startup, only start the container if it was already running before.
docker run --restart=unless-stopped app

# Always restart the container, regardless of the exit code. The container will also start when the Docker daemon starts, even if it was in a stopped state before.
docker run --restart=always app
```

Docker Compose uses a similar syntax since it only passes the options down to Docker CLI. Here are all possible restart policies you can use in a `docker-compose.yml` configuration file:

```yaml
# Demonstrating usage of all possible restart policies. You can only choose one per service.
services:
  app:
    # Do not automatically restart the container when it exits. This restart policy is the default, therefore omitting this configuration has the same result.
    restart: "no"

    # Only restart the container if it exits with a non-zero code.
    restart: "on-failure"

    # Same as above, but limit the number of restart attempts to 10.
    restart: "on-failure:10"

    # Always restart the container, regardless of the exit code. On Docker daemon startup, only start the container if it was already running before.
    restart: "unless-stopped"

    # Always restart the container, regardless of the exit code. The container will also start when the Docker daemon starts, even if it was in a stopped state before.
    restart: "always"		
```

### Restart delays
An essential property of automatic restarts is the delay time between restart attempts. In this regard, PM2 and Docker differ significantly in their default behaviour.

Unless configured otherwise, PM2 doesn't use a delay time between restart attempts. It also doesn't have a limit on the number of restart attempts. In the default scenario, PM2 will *immediately* restart your application for an *infinite* number of restart attempts. You can configure the delay time with `--restart-delay` and the maximum number of restart attempts with `--max-restarts`.

With automatic restarts enabled (`on-failure`, `unless-stopped` or `always` restart policy), Docker uses a delay time of 100ms. It also uses an exponential backoff multiplier of 2, *doubling* the delay time between restart attempts. The first restart will wait for 100ms, second restart 200ms, then 400ms, 800ms, 1600ms, and so on. The delay is *reset* after a container stays up for at least 10s.

PM2 allows you to configure an exponential backoff with the `--exp-backoff-restart-delay` flag. You set an initial delay time which will be multiplied by 1.5 after each attempt.

Even though you can't adjust this configuration further in Docker — unless using an orchestrator like Docker Swarm or Kubernetes — it's still important to be aware of how it works. By knowing the differences between PM2 and Docker, you'll avoid being caught by surprise in production environments.

## Conclusion
We've learned that PM2 will restart your application by default, and Docker requires you to be explicit. We also learned about the different restart policies that Docker offers and how they translate coming from a PM2 setup.

Even though you can't configure the delay time between restart attempts, Docker gives you *sane defaults* to work with. Knowing these differences between PM2 and Docker will increase your confidence in deploying to production environments.

Up next, in *[From PM2 to Docker: Cluster Mode](https://maximorlov.com/from-pm2-to-docker-cluster-mode/)* we look at scaling a Node.js application with Docker and how to make use of all available CPU cores.

Are you transitioning from PM2 to Docker? How is it going so far, and what issues are you facing? I'd like to hear from you! Let me know in the **comments below** ⬇️ or reach out to me on [**Twitter**](https://twitter.com/_maximization).

## Become a skilled Node.js developer

Every other Tuesday I send an email with tips on building solid Node.js applications. If you want to grow as a web developer and move your career forward with me, [drop your email here 💌](https://maximorlov.ck.page/0076aaf499).