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.
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
| Task | Command |
|---|---|
| Test the configuration | sudo nginx -t |
| Test and print the whole configuration | sudo nginx -T |
| Reload, no dropped connections (systemd) | sudo systemctl reload nginx |
| Restart (systemd) | sudo systemctl restart nginx |
| Reload if running, otherwise start | sudo systemctl reload-or-restart nginx |
| Status and the last log lines | systemctl status nginx |
| Reload without systemd | sudo nginx -s reload |
| Graceful stop: finish requests first | sudo nginx -s quit |
| Reopen log files after rotating them | sudo nginx -s reopen |
| Older init scripts | sudo service nginx reload |
| Alpine (OpenRC) | sudo rc-service nginx reload |
| macOS (Homebrew) | brew services restart nginx |
| Docker container | docker 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:
| reload | restart | |
|---|---|---|
| Signal | HUP to the master | Stop (QUIT, then TERM), then a fresh start |
| Master process | Keeps running | Replaced |
| Requests in progress | Finish on the old workers | Get about 5 seconds on Debian, then are cut |
| Listening ports | Stay open throughout | Closed for a moment: new visitors can get connection refused |
| Broken new config | Rejected; the old config keeps serving | nginx does not come back up |
| New nginx binary or library | Not picked up | Picked 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
sudo nginx -tnginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successfulnginx: [emerg] unknown directive "proxy_passs" in /etc/nginx/sites-enabled/example.com:14
nginx: configuration file /etc/nginx/nginx.conf test failednginx -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:
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(ornginxon 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_passto a dead port is valid configuration. The result is a 502 Bad Gateway on the first request. - Logic. A
server_namethat matches the wrong block or alocationthat 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:
| Message | Cause | Fix |
|---|---|---|
unknown directive "…" | A typo, or a directive from a module that is not loaded | Fix the spelling, or install the module package |
unexpected "}" | Usually a missing semicolon on the line before | Add the ; at the end of the previous directive |
"location" directive is not allowed here | A block in the wrong context, such as location outside server | Move it inside the right block |
cannot load certificate | Certificate path wrong or file missing | Check the path; ls -l the file as root |
host not found in upstream | proxy_pass names a hostname DNS cannot resolve | Use an IP address, or fix the name |
bind() … failed (98: Address already in use) | Another program owns the port | sudo ss -tlnp | grep ':80 ' |
conflicting server name … ignored | A warning: two blocks claim the same name | Not fatal; remove the duplicate |
open() "/run/nginx.pid" failed | nginx -s reload, but nginx is not running | sudo systemctl start nginx |
A failed systemd restart only prints a pointer to the details:
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.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 loggedCheck 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:
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-availableis live only with a symlink insites-enabled. Ifsudo nginx -Tdoes 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.comfrom 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.
ps -o pid,etime,args -C nginx 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 processA 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:
| Command | Signal | Effect |
|---|---|---|
nginx -s reload | HUP | Re-read the configuration, replace the workers gracefully |
nginx -s reopen | USR1 | Reopen the log files, used after log rotation |
nginx -s quit | QUIT | Graceful shutdown: finish current requests, then exit |
nginx -s stop | TERM | Fast 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 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 reloaddocker 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 insites-enabledis loaded as well, since Debian and Ubuntu include every file there, and its duplicateserverblock 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.comand 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.