Serve an SSR Astro app with NGINX

Let’s say you have a simple an Astro app with a Node adapter in order to server side rendering. Your astro.config.mjs file looks something like this:

import { defineConfig } from 'astro/config';
import node from '@astrojs/node';

export default defineConfig({
  output: 'server',
  adapter: node({
    mode: 'standalone',
  }),
});

Let’s say you’d like to be able to serve that app without having to use a service like Netlify.

The process is going to be essentially the same as serving a simple node app with nginx. Follow the steps there first.

Done?

Good - now let’s replace that simple node app with our Astro app.

First, make sure you replace 'standalone' with 'middleware' in the astro.config.mjs file as we’ll be using middleware mode. However, when I first tried this I did not notice any problem with using standalone mode even when utilizing Express as middleware when serving the app. But it is what Astro suggests doing so let’s just do it.

import { defineConfig } from 'astro/config';
import node from '@astrojs/node';

export default defineConfig({
  output: 'server',
  adapter: node({
    mode: 'middleware',
  }),
});

Push those changes to the remote repository.

Ssh into the server and clone the app into the home directory. Then install deps and build the app.

npm install
npm run build

Then cd into the repo and create a file called run-server.js. Add the following code to it:

import express from 'express';
import { handler as ssrHandler } from './dist/server/entry.mjs';

const app = express();
// Change this based on your astro.config.mjs, `base` option.
// They should match. The default value is "/".
const base = '/';
app.use(base, express.static('dist/client/'));
app.use(ssrHandler);

app.listen(3000); // this should be the same port that is being reverse proxied indicated in the nginx config file

Install Express.

npm install express

Then run the app with pm2.

pm2 start run-server.js

The app should be being served at the ip address of the server!