Skip to main content

Maintenance mode in Nginx

·565 words·3 mins
Work
Yuriy Semyenkov
Author
Yuriy Semyenkov
DevOps, tech, geek, mentor
This post is also available in Russian.

What is it?
#

There is no built-in “mode” like this in Nginx. I just use this name for the ability to manually switch the site to serve a specific page instead of the normal content.

Right now we can serve a specific HTML page instead of the default error page.

For example, instead of the default 504 error we can return a nice page with the company logo and a clear polite message like “The service is temporarily unavailable. We’re aware of the problem; please try again in a minute.”

error_page 500 501 503 502 504 /static/errors/5xx.html

What if we know there will be maintenance today from 20:00 and the service will be down? We turn on maintenance mode with information about the work and an ETA.

Second case: the service crashed hard, engineers are handling the incident, and users are seeing 500s, 503s, 504s, and timeouts. Turn on maintenance mode so everyone sees a stable message.

A not-so-good attempt
#

The first solution you’ll find online: use if to check for a file on the server (for example /etc/nginx/maintenance.flag). If the file exists — return the page; if not — continue with the standard Nginx config. Something like this:

server {
  	....
  	error_page 503 /maintenance.html;
	
  	location = /maintenance.html {
  	    root /etc/nginx/maintenance/;
  	    internal;
  	}
	
  	location / {
  	  if (-f /etc/nginx/maintenance.flag) {
  	     return 503;
  	  }
  	  proxy_pass http://127.0.0.1:3000;
  	}
}

This works, but here the file existence is checked on EVERY request. That can be wasteful. It looked fine in the dev environment, but caused slowdowns in production.

Why return 503?
#

Why should we return HTTP 503 even though we’re sending a normal, working HTML page?

503 means the service is temporarily unavailable (maintenance or overload). More details.

Search engines do not index pages that return 503. Imagine Google’s crawler hits your site during downtime and stores “Under maintenance” as the page content.

Also, browsers should not cache such pages, so after maintenance ends the user doesn’t keep seeing the maintenance page from cache.

How to do it with a config include?
#

The idea:

  • we have a file maintenance_on.conf with a single line: return 503;
  • when needed, include this file in the desired locations via include maintenance_on.conf
  • gracefully restart Nginx (reload)

Implementation details:

  • Create a directory to keep things clean: mkdir /etc/nginx/maintenance
  • Put your maintenance.html page there. Vibe-code it…
  • Create the config file maintenance_on.conf:
return 503;
  • Create an empty config file maintenance_off.conf
  • Create a symlink maintenance_current.conf
ln -sfn /etc/nginx/maintenance/maintenance_off.conf /etc/nginx/maintenance/maintenance_current.conf

Add a few lines to the Nginx server block:

server {
		...
    error_page 503 /maintenance.html;

    location = /maintenance.html {
        internal;
        root /etc/nginx/maintenance/maintenance.html;
        add_header Cache-Control "no-store" always;
        add_header Retry-After "3600" always;
    }

    location / {
        include /etc/nginx/maintenance/maintenance_current.conf;
        ...
    }
}

What happens here:

  • We tell Nginx to serve /maintenance.html on a 503 error.
  • We configure the /maintenance.html location that serves our service page. internal ensures the client cannot directly request /maintenance.html or any other internal file.
  • We add headers so the browser does not cache the page, plus Retry-After.
  • In the main (or any) location we include our symlink.

So to enable maintenance mode we only need to switch the symlink and gracefully reload Nginx:

ln -sfn /etc/nginx/maintenance/maintenance_on.conf /etc/nginx/maintenance/maintenance_current.conf
nginx -t && systemctl reload nginx

To disable it, point the symlink back to the _off file and reload Nginx again.

How to automate it — up to you.