Running on a server
On your computer the server starts with npm run dev and runs while the terminal is open. On a production server the API must be reachable from outside, restart itself after a crash and shut down cleanly during an update. Three settings take care of this.
The 0.0.0.0 address
By default listen accepts connections only from the same computer (localhost). On a server and in a Docker container you need to listen on all network interfaces — the address 0.0.0.0:
// src/server.js
import { buildApp } from './app.js';
const app = buildApp({ logger: true, trustProxy: true });
await app.ready();
await app.listen({ port: app.config.PORT, host: '0.0.0.0' });Now the API answers at the server's IP address. trustProxy: true is needed when nginx sits in front of Fastify: then request.ip holds the client address, not the proxy's.
Graceful shutdown
During an update the process manager sends the SIGTERM signal. If the process exits immediately, requests being handled at that moment are cut off. Add to the end of src/server.js:
// src/server.js
for (const signal of ['SIGINT', 'SIGTERM']) {
process.on(signal, async () => {
app.log.info({ signal }, 'Stopping the server');
await app.close();
process.exit(0);
});
}app.close() stops accepting new connections, waits for current requests and runs the onClose hooks — the database is closed there. Press Ctrl+C in the terminal: the log shows "Stopping the server", and the process exits without errors.
The PM2 process manager
PM2 watches the process: it restarts it after a crash and after a server reboot.
npm install --global pm2
pm2 start npm --name blog-api -- start
pm2 save
pm2 startuppm2 start runs the npm start script from package.json (with the --env-file flag), pm2 save remembers the process list, and pm2 startup prints a command that turns on autostart after a reboot.
| Command | What it does |
|---|---|
pm2 list | Processes and their state |
pm2 logs blog-api | Application logs |
pm2 restart blog-api | Restart after a code update |
pm2 stop blog-api | Stop |
Before launch
- create
.envwith the real secrets on the server; - install only production dependencies:
npm ci --omit=dev; - remove
pino-prettyfrom the logger settings; - put nginx with HTTPS in front of Fastify.
Common mistake
The server is running, curl http://localhost:3000 works on the server itself, but the API is unreachable from outside. Almost always the cause is listen without host: Fastify listens only on 127.0.0.1. Set host: '0.0.0.0' or pass the address through an environment variable.