July 12, 2021

How to Use Logrotate with Nginx in Docker

If you are using Docker with Nginx as a Configuration Management System, learn how to use Logrotate to prevent the log files from growing out of control.

Jump to The Scenario, The Problem, or The Solution

Let’s turn on the Geek Mode for this article, and have a quick look at the summary on how to logrotate logs with Nginx running under a Docker container.

The Scenario

There are many ways one can automate the configuration of services. For example:

- Using Docker containers in clusters such as Kubernetes.
- Using Docker containers as Configuration Management Systems, to achieve infrastructure-as-code in a host.

The following scenario fits when you are using Docker as Configuration Management Systems. It does not suit if you are using Docker in clusters like kubernetes.

Example of Dockerfile with Nginx

The goal is to automate the configuration of a web server which runs Nginx. You decide, as part of the automation strategy, to use Docker containers for some services in the host.

You have Nginx running with docker. There are many ways to achieve this goal. Let’s suppose that we have a Dockerfile such as the following:

FROM ubuntu:20.04

RUN apt-get update -y && apt-get install nginx nginx-extras -y

COPY nginx /etc/nginx
RUN chown :www-data /etc/nginx/
RUN rm -f /etc/nginx/sites-enabled/default

CMD ["nginx", "-g", "daemon off;", "-c", "/etc/nginx/nginx.conf"]

This Dockerfile is capable of running a custom Nginx configuration, which is copied from the folder Nginx to /etc/nginx in the container with the command COPY nginx /etc/nginx.

Exporting the Nginx logs to the host

If we are using Docker as a Configuration Management System in a context of Infrastructure-as-Code, and not in a cluster such as Kubernetes, it is advisable to have the logs visible in the host.

Therefore, when we run this container in production, the folder in the container at /var/log/nginx must be accessible at the host. The logs will also be persistent even when the container restarts.

This requires docker run to use the parameter --volume , -v. As an example, this command could be used to start the docker container:

docker run --rm --name nginx-container \
 -p 80:80 -p 443:443 \
 -v /var/log/nginx/:/var/log/nginx/ -d nginx-image

- The previous commands assume that you have generated an image of the previous Dockerfile and named it nginx-image.

- This running container binds the host ports 80 and 443.

- The logs at /var/log/Nginx/ are persistent and accessible from the host.

- The name of the running container is nginx-container

The previous example does not contemplate what actual website that you are exposing with Nginx. However, the example should be enough to understand the scenario.

The Problem

The advantage of this setup is rather than running Nginx in the host we are doing it in a container, effectively implementing an infrastructure-as-code approach.

The problem is that the logs in /var/log/nginx/ folder will eventually grow out of control, as there is no log rotation set up.

The Solution

If we decide to configure logrotate to manage the log rotation, there are three potential options:

1. Configure logrotate in the Nginx container

It has the advantage that all the configuration of Nginx would be self-contained in the Dockerfile. The disadvantage is that you are running more than one process in the container (you need to have cron daemon as well as Nginx), therefore you won’t be following Docker best practices.

2. Run logrotate in another Dockerfile, creating another Container with a very specific responsibility.

This would be consistent with the infrastructure-as-code approach, and it follows Docker best practices for containers, but somehow the container running logrotate needs to signal the container running Nginx to reopen the log files. I have not fully investigated this solution. Except for the container signal part, most of the configuration would be pretty similar to option 3.

3. Configure logrotate in the host.

The advantage is that it is easy to implement, the downfall is that you might not be following the infrastructure-as-code approach. However, in our case, we have another configuration management system that handles the host configuration (including the containers running in it). This is the solution that we have decided to implement.

Configuring logrotate with Nginx in the context of Docker containers

1 - Install logrotate:

apt-get install -y logrotate

2 - Add the configuration file for Nginx under Docker at the file /etc/logrotate.d/docker-Nginx

/var/log/nginx/*log {
 create 0644 root root
 rotate 52
    docker kill --signal="USR1" nginx-container

This configuration is pretty standard with logrotate. The only line which is worth mentioning is docker kill --signal="USR1" nginx-container.

According to Nginx documentation sending the signal USR1 to a Nginx process forces it to reopen the logs.

In the context of Docker, you can send a signal to the process running in the container with docker kill command.

As Docker manages the configuration of Nginx, and requires things such as binding the host ports 80 and 443, we know that we are not going to run more than one Nginx container in a host. This means that we also can define a meaningful name for the running container, which in this example is Nginx-container, allowing us to configure logrotate to signal the correct running container in the host.

To test the logrotate configuration:

1 - Make a dry run of this configuration with the following command:

logrotate -d /etc/logrotate.d/docker-nginx

2 - Once the dry run works as expected, force the execution of logrotate to fully confirm that is working and that there is no undesired side effects.

logrotate -f  /etc/logrotate.d/docker-nginx

That’s all folks!