# Deploying to Production

Deploying a Nodewood project to a production server is relatively easy, if you are comfortable with the JavaScript ecosystem. There are a couple unique steps, designed to save you time in the long run, but the rest will be familiar to you if you have deployed a JavaScript application to production before.

Deployment methods:

# Manual Deployment

Sometimes, you just have to get in there and build your own deployment system. You may be restricted to certain hosts or systems that prevent you from using other methods, or you may just want to know intimately the details of how your application runs. These instructions are for you.

# Server Preparation

Before you begin deploying your application, you will need to ensure your server has certain applications and libraries installed:

  • PostgreSQL (opens new window): You will either need to install this yourself or used a hosted installation from your hosting provider. The latter is recommended, since properly administering a database server with respect to performance and security is a topic unto itself.
  • Node (opens new window): Fairly obviously, you will need to ensure that Node is installed. Version 14 (LTS) is recommended.
  • Yarn (opens new window): Yarn is used to install packages and run Nodewood.
  • Nginx (opens new window): This is the web server that will take requests and redirect them to your application.

Next, you will need to ensure certain JavaScript packages are globally installed:


You can install these packages with a single command: yarn global add pm2 knex @nodewood/cli.

# First-Time Deployment

The first time you deploy your code will have a few extra steps, compared to subsequent deployments.

  1. Install your code. I find that it's simplest to use git to checkout your code, and then you can just run git pull when you need to do updates. For bonus points, you can generate and use an RSA key to checkout your code, so you don't need to enter your password every time you check out your code in the future.
  2. Add your production .env file. This will be very similar to your development .env file. The main difference is that you will want to replace any development keys and settings for production settings. You will need to add it to two locations: your code root (/code/.env) and your application folder (/code/app/.env). This is because the API and webpack expect it to be in different locations.
  3. Add your production knexfile.js file. This is the file that tells Knex how to connect to your database. Again, simply replace with your production settings, which you should be able to copy from your .env file.
  4. Copy your .nodewood.js file.
  5. Install your node modules with yarn install from the project root.
  6. Run your database migrations with yarn migrate from the project root.
  7. Run your Stripe migrations with nodewood stripe:sync from the project root. Make sure you have production Stripe keys set in your .env file.
  8. Build your UI with NODE_ENV=production yarn build-ui from the project root.
  9. Start the API service using PM2 with pm2 start app/api/api.js.
  10. Save the PM2 configuration with pm2 save.
  11. Ensure PM2 starts with your configuration at system startup with pm2 startup.
  12. Purchase/generate SSL certificates and install them. You can either purchase certificates good for a year or more from various vendors, or use Let's Encrypt (opens new window) to generate free SSL certificates. If you go the Let's Encrypt route, you will likely want to set up their auto-renewing certificate bot, as their certificates only last on the order of months, for increased security.
  13. Configure Nginx to redirect web traffic to your app. (Examine wood/nginx-default.conf for reference.)

# Subsequent Deployments

Every subsequent deployment will be simpler, with just the following steps:

  1. Update your code. (This is where it helps to have used git before, since you can just run git pull.)
  2. Install any new node modules with yarn install from the project root.
  3. Run your database migrations with yarn migrate from the project root.
  4. Run your stripe migrations with nodewood stripe:sync from the project root.
  5. Rebuild your UI with NODE_ENV=production yarn build-ui from the project root.
  6. Restart your API with pm2 restart api.

# Pulumi Deployment

Pulumi (opens new window) is an Infrastructure as Code tool that allows you to describe your desired infrastructure as a program that is then evaluated to determine what services should be created and configured to enable that. Later, as your infrastructure grows and changes, you can update your code, and Pulumi will handle creating and evaluating the difference between your actual and desired infrastructure.

Nodewood provides a "starter" script that you can use to define your infrastructure on AWS. It creates a Fargate service that runs a deployment version of the api container, a load balancer that stands in front of it, allowing you to scale to multiple instances, an RDS database instance, and the required remaining infrastructure to connect them. Pointing your domain at the load balancer instance is as simple as setting a value on your domain's nameserver. Nodewood recommends using Cloudflare for this, as it simplifies the process significantly, even applying SSL to the resulting connection.

# Preparation

# Set up Pulumi

  1. Create a Pulumi account at https://app.pulumi.com/signup (opens new window).
  2. From your host machine, log in to Pulumi with pulumi login.
  3. Make sure you've ejected wood/docker/docker-compose.yml, then edit your app/docker version and uncomment the first commented-out volume in the run service. This will share your local Pulumi credentials with the run container.

# Set up AWS

  1. Download and install the AWS CLI tool: https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html (opens new window)
  2. From your host machine, configure the AWS CLI tool with aws configure.
  3. Make sure you've ejected wood/docker/docker-compose.yml, then edit your app/docker version and uncomment the second commented-out volume in the run service. This will share your local AWS credentials with the run container.

# Set up CloudFlare

  1. Create a CloudFlare account (if you don't already have one) at https://dash.cloudflare.com/sign-up (opens new window).
  2. Under the "Websites" section, click "+ Add a Site" and follow the instructions to add your site and change its nameservers to use CloudFlare.

# Customize your deploy scripts

  1. Copy wood/deploy to app/deploy and read through the code in the lib folder. You will need to understand what it is doing, since it is setting up your infrastructure, and you will need to modify it so that any customization you require is performed. This code will control the entirety of your infrastructure, so you'll want to understand it well.
  2. Copy prod.env.template to prod.env and fill in the details.
  3. Make sure to set skipFinalSnapshot: false and deletionProtection: true in rds.js when deploying your production infrastructure for real. This will ensure you can't accidentally delete your database by destroying the infrastructure, and that a final snapshot of the database will still be kept when you do delete it.

# First-time Deployment

  1. Run nodewood script cli/scripts/PulumiDeployScript. Take note of the Database URL and Load balancer URL output by this script.
  2. Go to the "DNS" section in CloudFlare for the site you set up in the Preparation section.
  3. Click "+ Add Record".
  4. Choose a type of "CNAME", a name of "@", and a target of the Load balancer URL value from step (1), and save.
  5. Wait for your domain to resolve, and test your site!

# Subsequent Deployments

  1. Run nodewood script cli/scripts/PulumiDeployScript.

# Notes & Troubleshooting

  • Migrations are automatically run as part of the PulumiDeployScript, but if you wish to manually connect to the database, use the Database URL that is output when running this script.
  • Pulumi is still in active development and can be unstable on MacOS with M1/2 processors. If you are running Pulumi commands directly on an M1/2 machine and they fail, you can safely re-run them until they succeed.
  • If you are getting Pulumi upgrade warnings when running your deploy, this is entirely okay. However, if you wish to clear these warnings, simply delete the run Docker image. Then, the next time you deploy, it will rebuild the image, installing the latest version of Pulumi.