Guides

How to restart or reload nginx, and when a reload is not enough

Restart nginx with sudo systemctl restart nginx, or reload it without dropping connections. Run nginx -t first, read the errors, reload nginx in Docker.

Published ~8 min read
On this page

Short answer: sudo systemctl restart nginx restarts nginx, but after a configuration change the better command is sudo nginx -t && sudo systemctl reload nginx. The reload applies the new configuration without dropping a single connection, and the -t in front makes sure a typo never reaches the running server. Keep the full restart for the cases a reload cannot handle: nginx is not running, the nginx binary or OpenSSL was upgraded, or the systemd unit changed.

Debian · Ubuntu · Rocky · Alma
sudo nginx -t && sudo systemctl reload nginx    # after editing the config
sudo systemctl restart nginx                     # full stop and start
systemctl status nginx                           # did it work?

The behaviour described here comes from nginx.org’s own documentation (the beginner’s guide and “Controlling nginx”) and from the systemd unit file in Debian 12’s nginx package, version 1.22.1. The output samples show the format nginx prints; the file names in them are examples.

Restart and reload commands at a glance

TaskCommand
Test the configurationsudo nginx -t
Test and print the whole configurationsudo nginx -T
Reload, no dropped connections (systemd)sudo systemctl reload nginx
Restart (systemd)sudo systemctl restart nginx
Reload if running, otherwise startsudo systemctl reload-or-restart nginx
Status and the last log linessystemctl status nginx
Reload without systemdsudo nginx -s reload
Graceful stop: finish requests firstsudo nginx -s quit
Reopen log files after rotating themsudo nginx -s reopen
Older init scriptssudo service nginx reload
Alpine (OpenRC)sudo rc-service nginx reload
macOS (Homebrew)brew services restart nginx
Docker containerdocker exec web nginx -s reload

Reload vs restart: what happens to open connections

nginx runs as one master process and several worker processes. The workers handle the traffic; the master reads the configuration and manages the workers. The two commands treat them very differently:

reloadrestart
SignalHUP to the masterStop (QUIT, then TERM), then a fresh start
Master processKeeps runningReplaced
Requests in progressFinish on the old workersGet about 5 seconds on Debian, then are cut
Listening portsStay open throughoutClosed for a moment: new visitors can get connection refused
Broken new configRejected; the old config keeps servingnginx does not come back up
New nginx binary or libraryNot picked upPicked up

On a reload, the master checks the new configuration and tries to apply it. If that works, it starts new workers with the new configuration and tells the old ones to shut down gracefully: they stop accepting new connections, finish the requests they are serving, and exit. If the new configuration cannot be applied, the master rolls back and carries on with the old one. Nobody notices a reload, which is why it is the default for every configuration change, a renewed TLS certificate included.

A restart is a stop followed by a start. Debian’s unit file spells out the stop: it sends QUIT (graceful), waits up to 5 seconds, then has systemd send TERM (fast shutdown), and after another 5 seconds SIGKILL. A long download or a WebSocket connection does not survive that, and for the moment between stop and start nothing listens on ports 80 and 443. Worse, if the configuration on disk is broken, the stop succeeds and the start fails: the site is down until you fix it. A failed reload never takes the site down. A failed restart always does.

Always run nginx -t first

any Linux
sudo nginx -t
Example output · valid configuration
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Example output · a typo
nginx: [emerg] unknown directive "proxy_passs" in /etc/nginx/sites-enabled/example.com:14
nginx: configuration file /etc/nginx/nginx.conf test failed

nginx -t parses the full configuration with every included file, loads the certificates, and names the file and line of the first problem it finds. Chaining it with && means the reload only runs when the test passed. Run it with sudo: as a normal user it fails with Permission denied on the error log and the PID file even when the configuration is fine. sudo nginx -T also prints the complete merged configuration, the quickest way to see which file really sets a directive:

any Linux
sudo nginx -T 2>/dev/null | grep -n "server_name"

What nginx -t does not catch

  • A port another program holds. The test does not complain when Apache or a second nginx already listens on port 80; the start does, with bind() to 0.0.0.0:80 failed (98: Address already in use). See Address already in use.
  • Permissions of the worker user. The test runs as root; the workers run as www-data (or nginx on Rocky and Alma). A file root can read and the workers cannot passes the test and fails at request time, usually as a 403 Forbidden.
  • A backend that is not running. proxy_pass to a dead port is valid configuration. The result is a 502 Bad Gateway on the first request.
  • Logic. A server_name that matches the wrong block or a location that never wins is perfectly valid syntax.

Reading the errors

When the test or a restart fails, the message nearly always names the cause. The ones that come up most:

MessageCauseFix
unknown directive "…"A typo, or a directive from a module that is not loadedFix the spelling, or install the module package
unexpected "}"Usually a missing semicolon on the line beforeAdd the ; at the end of the previous directive
"location" directive is not allowed hereA block in the wrong context, such as location outside serverMove it inside the right block
cannot load certificateCertificate path wrong or file missingCheck the path; ls -l the file as root
host not found in upstreamproxy_pass names a hostname DNS cannot resolveUse an IP address, or fix the name
bind() … failed (98: Address already in use)Another program owns the portsudo ss -tlnp | grep ':80 '
conflicting server name … ignoredA warning: two blocks claim the same nameNot fatal; remove the duplicate
open() "/run/nginx.pid" failednginx -s reload, but nginx is not runningsudo systemctl start nginx

A failed systemd restart only prints a pointer to the details:

Example output
Job for nginx.service failed because the control process exited with error code.
See "systemctl status nginx.service" and "journalctl -xeu nginx.service" for details.
Debian · Ubuntu
sudo journalctl -u nginx -n 30 --no-pager      # what systemd and nginx -t reported
sudo tail -n 30 /var/log/nginx/error.log       # what the running master logged

Check the error log after a reload as well. The reload command does not always report a configuration the master could not apply: a port that is already taken, for example, shows up only as an [emerg] line there, while the old configuration quietly keeps serving. journalctl covers filtering the systemd side by time and severity.

When a reload is not enough

After upgrading nginx or OpenSSL

A reload starts new workers from the master that is already running, so it keeps the binary and the libraries that master loaded. If an apt upgrade replaced nginx or OpenSSL and did not restart nginx itself, the old code keeps running. Check:

Debian · Ubuntu
sudo readlink /proc/$(cat /run/nginx.pid)/exe
sudo grep -E '\.so[.0-9]* \(deleted\)' /proc/$(cat /run/nginx.pid)/maps

/usr/sbin/nginx (deleted) from the first command, or any line from the second (a library such as libssl.so.3 marked deleted), means the files on disk are newer than what is running: restart. The needrestart package does this check for every service after an upgrade. nginx.org also documents a binary swap without downtime (USR2, then WINCH and QUIT to the old master), which is rarely worth it on a single server.

After changing the systemd unit

Limits and environment set in the unit, such as LimitNOFILE= in an override file, apply to the master when it starts. Run sudo systemctl daemon-reload, then restart; a reload keeps the old master and its old limits. The systemctl guide explains when daemon-reload is needed.

nginx is not running, or not answering

A reload needs a running master. If nginx is stopped, systemctl reload fails and nginx -s reload complains about the PID file; use systemctl start or reload-or-restart. If workers hang and a reload changes nothing, a restart is the honest fix. Also check that nginx was started by systemd in the first place: an nginx started by hand with sudo nginx is invisible to systemctl, and a later systemctl start fails on the ports the first one holds. Stop the stray one with sudo nginx -s quit, then start it through systemd.

The reload worked, but the change seems ignored

  • The file is not included. On Debian and Ubuntu, a site in sites-available is live only with a symlink in sites-enabled. If sudo nginx -T does not show your change, nginx never read it.
  • A cache answers. The browser, a CDN or a proxy in front can serve the old response. Test with curl -sI https://example.com from the server or another machine.
  • Old workers still hold long connections. WebSockets and long downloads stay on the old workers, with the old configuration, until they end.
any Linux
ps -o pid,etime,args -C nginx
Example output
    PID     ELAPSED COMMAND
    812  3-02:11:40 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
  20114       01:12 nginx: worker process is shutting down
  20391       00:05 nginx: worker process
  20392       00:05 nginx: worker process

A worker is shutting down is an old one still finishing its connections. The worker_shutdown_timeout directive (nginx 1.11.11 and later, in the main context of nginx.conf) caps how long it may take, for example worker_shutdown_timeout 30s;.

nginx -s reload vs systemctl reload

Both send the same signal to the same master. On Debian, the unit’s reload command runs nginx -s reload itself. On a systemd server, use systemctl anyway: it keeps systemd’s view of the service in step and logs the event in the journal. nginx -s is for systems without systemd and for containers. Its four signals, per nginx.org:

CommandSignalEffect
nginx -s reloadHUPRe-read the configuration, replace the workers gracefully
nginx -s reopenUSR1Reopen the log files, used after log rotation
nginx -s quitQUITGraceful shutdown: finish current requests, then exit
nginx -s stopTERMFast shutdown: exit now

Reloading nginx in Docker

In the official nginx image, nginx runs in the foreground as the container’s main process. Test and reload inside the container rather than restarting it:

Docker
docker exec web nginx -t
docker exec web nginx -s reload
docker kill --signal=HUP web                 # the same reload, sent by Docker

# Compose: service name instead of container name
docker compose exec nginx nginx -t && docker compose exec nginx nginx -s reload

docker restart web is the restart equivalent, with the same downside: if the configuration is broken, the container exits right after it starts, and docker logs web shows the [emerg] line. One trap is specific to containers. If you mount a single file, as in -v ./nginx.conf:/etc/nginx/nginx.conf:ro, and your editor saves by writing a new file and renaming it over the old one, the container keeps seeing the old file: a bind mount follows the original inode. The test passes, the reload changes nothing. Mount the directory instead (./conf.d:/etc/nginx/conf.d:ro), or restart the container after such an edit.

A safe routine for every change

  • Back up the file before editing, to a place nginx does not read: sudo cp /etc/nginx/sites-available/example.com /root/example.com.bak. A copy left in sites-enabled is loaded as well, since Debian and Ubuntu include every file there, and its duplicate server block then competes with the real one.
  • Test, then reload, in one line: sudo nginx -t && sudo systemctl reload nginx.
  • Check from outside: curl -sI https://example.com and a look at the error log.
  • Restart only for the cases above, and at a quiet moment.

The reverse proxy setup this is usually about, with certificates and WebSockets, is in the nginx reverse proxy guide.

Frequently asked

What is the difference between nginx reload and restart?

A reload (systemctl reload nginx, or nginx -s reload) sends the master process a HUP signal. It starts new workers with the new configuration while the old workers finish their requests, so no connection is dropped, and a configuration it cannot apply is rolled back while the old one keeps serving. A restart stops nginx completely and starts it again, which cuts open connections and leaves nginx down if the configuration is broken.

How do I restart nginx on Ubuntu or Debian?

Test first with sudo nginx -t, then run sudo systemctl restart nginx. For a configuration change, sudo nginx -t && sudo systemctl reload nginx is enough and does not drop connections. systemctl status nginx shows whether it worked, and journalctl -u nginx shows why it did not.

Does nginx -s reload drop connections?

No. The old worker processes stop accepting new connections but finish the requests they are serving before they exit, while new workers take the new connections. Long-lived WebSocket connections stay on the old workers until they close, or until worker_shutdown_timeout ends them.

How do I reload nginx in a Docker container?

Run docker exec CONTAINER nginx -t, then docker exec CONTAINER nginx -s reload, or send the same signal with docker kill --signal=HUP CONTAINER. If you bind-mount a single config file, mount its directory instead: an editor that saves by replacing the file leaves the container reading the old version.

Related articles

All guides