Redwood v2.0.0 is now available!

Don’t panic

Two point oh?! What is happening?! Reading Tom’s latest blog post should ease some of your worries: Major Version Numbers are Not Sacred. Along with v1, this major version will be a part of the same Redwood epochal version, Arapaho Forest.

There are only a few breaking changes in this release, and if you don’t use some of these features then this’ll be practically the same as a normal ol’ minor-version update for you!

Having said that, here are the three breaking changes:

Breaking Changes

Linting import order

:information_source: This is a small change but affects all users.

Previously, Redwood didn’t validate the order of imports. Import something from a local file before an npm package? Space between import groups? Nothing to see here:

But since we use this rule in the framework itself, and since Redwood’s opinionated, we consider its exclusion from projects a miss. So as of v2.0.0, Redwood lints the order of imports and reports them as an error. So if you’re seeing red squiggles on import declarations after upgrading, this’s why:

Although they’re annoying and make you feel like you did something wrong, in of themselves, these squiggles are harmless. The real consequences come if you’re running lint in CI because now that step will fail. But luckily it’s a super easy fix.

How to upgrade

TL;DR, after upgrading, run yarn rw lint --fix.

Although it really is that simple, we recommend going the extra mile to keep your git history clean by stashing whatever changes you have, running the command, then committing the changes:

# after stashing, or at least when your working directory is clean
yarn rw lint --fix
git add .
git commit -m "style: order imports"

Note that running yarn rw lint --fix may alert you to other errors, like unused variables, that the lint command can’t fix automatically. That’s ok—it should’ve fixed the order of imports still since it’s just a matter of rearranging import declarations.

Congratulations, you’re on v2.0.0! But if you’re deploying via baremetal or using auth in serverless functions, keep reading.

Baremetal deploy

:information_source: If you don’t use the baremetal deploy strategy, this doesn’t affect you. You can safely skip this section. Otherwise, expand the “Details” section and follow along.

This release features some major updates to the Baremetal deploy option! Let’s break them down along with any changes you’ll need to make to your server and deploy.toml file.

New Code Update Strategy

We’ve got a good news/bad news situation. The good news: you can now deploy a branch other than main and code updates are much more reliable using git clone! The bad news is that you’re going to have to do some more config and server setup to get this latest version working. But once you do, everything will be awesome!


Baremetal now requires that PM2 be installed globally, rather than a development dependency of your app. This has to do with the new directory structure we’re going to create and where pm2 is executed from (short version: it’s run outside of your actual codebase, so yarn pm2 won’t be available).

You’ll want to remove your existing pm2 services. This will cause downtime, so be mindful if running in a production environment. Run the following commands on the server:

yarn pm2 delete all

Now install PM2 globally: PM2 - Installation | Guide | PM2 Documentation

PM2 will still be running in memory as the previous yarn pm2 version, so you can run this command to update the process:

pm2 update

If you previously setup PM2 to run at startup, you’ll need to update to the new global install instead:

yarn pm2 unstartup
pm2 startup

Be sure to remove pm2 in your app’s package.json file.

Directory Structure

Next we’ll need to update the directory structure inside your deploy path.

Current directory structure:

└── var
    └── www
        └── myapp
            ├── api
            ├── web
            ├── ...

New directory structure:

└── var
    └── www
        └── myapp
            ├── .env <────────────┐
            ├── current ────────┐ │
            └── 20220420120000 <┘ │
                ├── .env ─────────┘
                ├── api
                ├── web
                ├── ...

The current symlink is updated at the end of a deploy to point to the latest release (assuming a successful clone and build). This means all build packages in web/dist and api/dist remain self-contained in their parent timestamp directory. This makes a rollback trivial: just point the current symlink to the previous directory and restart your web/api processes! yarn rw deploy baremetal --rollback is coming soon! A shared .env file exists in the root of your app path and each copy of the codebase will contain a symlink back out to this file.

The easiest way to update to this structure is to simply remove everything inside of your currently deploy directory (like /var/www/myapp) and let yarn rw deploy baremetal --first-run set everything up for you. You’ll want to keep your .env file, however:

cd /var/www/myapp
mv .env ..
rm -rf *
mv ../.env .

Config Updates

You’ll need to add a new option to your ecosystem.config.js file for any processes that run within the context of your app:

module.exports = {
  apps: [
      name: 'serve',
+     cwd: 'current',
      script: 'node_modules/.bin/rw',
      args: 'serve',
      instances: 'max',
      exec_mode: 'cluster',
      wait_ready: true,
      listen_timeout: 10000,

You’ll also need to add the repo config setting and optionally branch to your deploy.toml file (also note the new environment name prefixing servers, more on that in a moment):

host = ""
username = "user"
agentForward = true
sides = ["api", "web"]
path = "/var/www/myapp"
processNames = ["api"]
+ repo = ""
+ branch = "main"

The branch will default to main if not set.

Make sure everything is saved, committed, and pushed up to your repo because we’re ready for the first deploy.

First Deploy

Run the deploy command for the first time to setup the directory structure, check out your code, and build and start monitoring your processes:

yarn rw deploy baremetal --first-run

And that’s it! If everything started up correctly then you’re good to go for future deploys. If not check out

If you want to perform a one-time deploy of a branch other than the one listed in deploy.toml you can do so via a flag at deploy time:

yarn rw deploy baremetal --branch=staging

Multiple Environment Support

Baremetal deploy now supports deploying to multiple environments! If you’ve got production, and staging, and whatever else, this update is for you. If you’re setting up a new deploy from scratch with yarn rw setup deploy baremetal you’re good to go. If you have an existing deploy.toml file you may want to make an update:

Instead of just [[servers]] prefixing your server config, make it [[environment.servers]] where environment is the actual name of your environment. For example:

host: ''
# remaining config...

host: ''
# remaining config...

You can still have multiple servers in an environment, just repeat the [[servers.production]] heading multiple times, one for each block of server config:

host: ''
# remaining config...

host: ''
# remaining config...

This new syntax is optional: keeping your default [[servers]] name will act as production (and leaving off the stage name in yarn rw deploy baremetal will default to use these server settings).

Maintenance Page

You can now enable/disable a maintenance page with the Baremetal deploy! Simply create web/src/maintenance.html containing your maintenance message. (For now this page must just be plain HTML, but if the need is there it should be possible to add the page to the webpack/babel pipeline and make it a full React app. If this is something you’d like to work on, get in touch!)

When you’re ready to enable your maintenance page on the site:

yarn rw deploy baremetal --maintenance up

And to take it back down:

yarn rw deploy baremetal --maintenance down

This command respects the environment argument so you can put it up on just your staging environment, for example:

yarn rw deploy baremetal staging --maintenance up

That’s it! This feature works by turning web/dist/index.html into a symlink pointing to web/src/maintenance.html. As long as your server is configured to check for the existence of 200.html for the web side and serve that for any URL request that isn’t the API, users should see the maintenance page the time time your site loads. Note that if a user already has your site loaded, they will most likely be able to keep clicking through your pages since everything is already cached in the browser. We’re thinking about adding a “ping” feature that, on each page request, would check with the server to see if the maintenance page should be displayed, and if so interrupt their browsing session to show it. If this is something you’d like to work on, let us know by opening an issue or PR!

Old Deploy Cleanup

Baremetal will now cleanup old deploy codebase directories at the end of a deployment! It defaults to keep the last 5 deployments, but this can be configured in deploy.toml with the keepReleases option:

host = ""
username = "ubuntu"
agentForward = true
sides = ["api", "web"]
path = "/var/www/myapp"
processNames = ["api"]
repo = ""
branch = "main"
+ keepReleases = 5

Why keep around old deploys? In case something goes horribly wrong and you need to rollback!

This config setting is optional and will default to keeping the last 5 deploys if not set.


If you deploy and find something has gone horribly wrong, you can now rollback your deploy to the previous release!

yarn rw deploy baremetal --rollback

You can even rollback multiple deploys, up to the total number you still have denoted with the keepReleases option:

yarn rw deploy baremetal --rollback 3

Note that this will not rollback your database—if you had a release that changed the database, that updated database will still be in effect, but with the previous version of the web and api sides. Trying to undo database migrations is a very difficult proposition and isn’t even possible in many cases.

Make sure to thoroughly test releases that change the database before doing it for real!

There’s no Rollforward: you probably rolled back because the newer deploy is faulty! Performing a new deploy would be the equivalent action to rolling the codebase forward to a newer release.

Override Default yarn and pm2 commands

You can now override the default yarn and pm2 exec commands with your own custom command. Why would you want to do this? Tools like doppler provide their own CLI command to inject secrets into your shell, and you then chain on the command you would normally run, like so:

doppler run -- yarn rw prisma migrate deploy

To set this, add the following to your deploy.toml file:

host = ""
username = "user"
agentForward = true
sides = ["api","web"]
path = "/var/www/app"
processNames = ["serve"]
repo = ""
branch = "main"
keepReleases = 5
+ packageManagerCommand = "doppler run -- yarn"
+ monitorCommand = "doppler run -- pm2"

These two settings are optional and will default to the existing yarn and pm2 commands if not set.

Lifecycle Events

When in the course of a deploy, if it becomes necessary to run additional commands not provided for in the stock list of Baremetal steps, you can turn to lifecycle events. These events allow you to run custom commands before or after the existing commands. Here are the current steps in a deploy:

  1. update - the latest code is cloned from the server
  2. symlinkEnv - a symlink is created in the newly cloned codebase pointing to the .env file in the main app directory
  3. install - latest dependencies are installed
  4. migrate - database and db migrations are run
  5. build - the api and/or web sides are built
  6. symlinkCurrent - a symlink is created from the main app directory to the latest deploy directory
  7. restart - PM2 restarts any services listed
  8. cleanup - old deploy directories are removed

There are three ways you can define your lifecycle events: globally (runs for all environments and all servers), environment-specific (runs for all servers in a given environment) or server-specific (runs only for a single server).

Global Lifecycle Events

Add a top level [before] or [after] key to your deploy.toml, listing which step you want to hook into and what command to run:

install = 'touch install.lock'

install = 'rm install.lock'

host = ''
# ...

You can hook into multiple steps, and execute multiple custom commands:

install = ['touch install.lock', 'rm logs/deploy.log']
cleanup = 'echo "Removing old deploys" > "deploy.log"'

host = ''
# ...

Environment Lifecycle Events

install = 'touch install.lock'

build = 'touch debug-enable'

host = ''
# ...

host = ''
# ...

Server Lifecycle Events

host = ''
before.install = 'touch install.lock'
# ...

Important Considerations

All commands are run inside the newly deployed code directory (like /var/www/myapp/20220520120000) except update and cleanup! These are run in the path defined in deploy.toml since they are independent of any single release.

The build and restart steps may run multiple times based on how many sides you’re building or services you’re restarting. Any defined before/after lifecycle events will run as many times as the original step is run, so plan accordingly: make your command idempotent so that it can be run multiple times without side effects.

Auth in serverless functions

:information_source: If you don’t use useRequireAuth in serverless functions, this doesn’t affect you. You can safely skip this section. Otherwise, expand the “Details” section below and follow along.

Previously, the behavior of useRequireAuth in serverless functions differed depending on whether auth-related headers were set. In this release, we rectified it so that it behaves the same whether or not headers are set.

When calling a serverless function wrapped in useRequireAuth with no auth-related headers set, Redwood continues invoking the serverless function. But if the auth-provider header is set and the token is invalid, the function throws and returns a 401 error. This is inconsistent behavior for otherwise similar scenarios. In v2.0.0, we’ve changed it so that in both scenarios it continues executing.

Note that this breaking change is just behavioral. It’s up to you as the developer to make sure you call requireAuth or use the isAuthenticated variable inside the serverless function to enforce authentication. That is, useRequireAuth just makes the authentication context available, but doesn’t enforce authentication in of itself.

:information_source: Note that if you think the name useRequireAuth is confusing, we do too and plan on renaming it. We couldn’t settle on a better alternative in time for this release, otherwise we would’ve included it.


Unique contributors: 35

PRs merged: 48


  • Move import/order rule into shared eslint configuration #4954 by @nickpdemarco
  • useRequireAuth: Warn on catch #5202 by @Tobbe
  • Baremetal MEGA PR #5500 by @cannikin


  • fix(storybook): support mdx #5456 by @virtuoushub
  • docs: Link to current document source. #5658 by @kunalarya


  • Fix dbAuth on AWS Lambda #5474 by @jonasoberschweiber
  • fix: Handle undefined public key for Jwks request in auth0 decoder #5701 by @dthyresson
  • fix: RedwoodLogger – Mask errors, but include stack trace in formatter #5704 by @dthyresson
  • Lint fix some template files #5766 by @jtoar


  • Fix and rebuild test-project fixture #5602 by @dac09


  • Fix minor issues with the docs/tutorial #5522 by @Avataw
  • fix(docs): Add note about TEST_DATABASE_URL #5544 by @Isaac-Tait
  • Fix typo #5593 by @mparramont
  • Add warning box for missing roles in authorization doc #5611 by @Avataw
  • Fix typo in Tutorial Chapter 1 Prerequisites #5616 by @mattdriscoll
  • docs: Fix Typescript typo in authentication chapter #5621 by @kunalarya
  • yet another batch of typo squashing #5631 by @AntonioMeireles
  • Update #5637 by @fakkio
  • docs: Details for TS generics when mocking queries #5645 by @callingmedic911
  • Fix typo in RBAC docs #5652 by @jordanrolph
  • fix: The error was caused by not declaring the variable $id. #5660 by @OkazakiRui
  • Update contributing docs #5685 by @jorgepvenegas
  • :memo: Add documentation for custom input fields #5692 by @craineum
  • Fix typos in docs #5694 by @minho42
  • Fix typo in ArticleCell code snippet of Tutorial 2 docs #5695 by @Leon-Sam
  • docs: Removes unnecessary word in Chapter 3: #5698 by @fangpinsern
  • docs: Fix typo with articles in Routing Params docs #5699 by @Gombeng
  • docs: yet another single byte typo squash #5702 by @AntonioMeireles
  • Update #5706 by @Masvoras
  • Fix typo #5711 by @andershagbard
  • fix: typo in generate service command #5712 by @alephao
  • docs: (typo fix) Updated reference to GraphQL Yoga from Apollo #5731 by @rushabhhere
  • Fixed not enough spaces after ‘>’ #5732 by @AlonHor
  • Fix typo #5733 by @ubugnu
  • Fix typo #5735 by @razzeee
  • docs: Some fact checks in the deployment section. #5736 by @rushabhhere
  • docs: GraphQL Yoga’s playground, not Apollo Server’s #5741 by @rushabhhere
  • docs: Fix not being able to fullscreen YouTube embeds #5744 by @jaaneh
  • Correct a few spelling mistakes #5747 by @andershagbard
  • docs: yet another set of single byte typo squashing #5749 by @AntonioMeireles
  • Adds mailchimp signup form to tutorial Foreword page #5758 by @cannikin
  • docs: adding precisions and fixes to contributing #5760 by @MrGuiMan
  • Adds note about errors when generating cells without a SDL file #5764 by @cannikin
  • docs(graphql): replace outdated mentions to Apollo Server #5768 by @charlypoly
  • Fix typo in tutorial #5686 by @paikwiki

Package Dependencies

View all Dependency Version Upgrades
  • fix(deps): update dependency react-player to v2.10.1 #5465 by @renovate
  • fix(deps): update typescript-eslint monorepo to v5.25.0 #5579 by @renovate
  • fix(deps): update dependency copy-webpack-plugin to v11 #5580 by @renovate
  • fix(deps): update dependency msw to v0.40.0 #5581 by @renovate
  • fix(deps): update storybook monorepo to v6.5.0 #5583 by @renovate
  • fix(deps): update dependency css-minimizer-webpack-plugin to v4 #5586 by @renovate
  • fix(deps): update dependency eslint-plugin-react to v7.30.0 #5588 by @renovate
  • chore(deps): update dependency @nhost/hasura-auth-js to v1.1.9 #5594 by @renovate
  • chore(deps): update dependency @nhost/nhost-js to v1.1.13 #5595 by @renovate
  • fix(deps): update storybook monorepo to v6.5.3 #5596 by @renovate
  • chore(deps): update dependency npm-packlist to v5.0.4 #5597 by @renovate
  • fix(deps): update dependency msw to v0.40.1 #5598 by @renovate
  • fix(deps): update graphql-tools monorepo #5599 by @renovate
  • chore(deps): update dependency @nhost/nhost-js to v1.1.14 #5603 by @renovate
  • chore(deps): update dependency @clerk/types to v2.14.0 #5604 by @renovate
  • fix(deps): update dependency webpack-retry-chunk-load-plugin to v3.1.1 #5606 by @renovate
  • chore(deps): update dependency @clerk/clerk-js to v3.12.0 #5607 by @renovate
  • chore(deps): update dependency @clerk/clerk-sdk-node to v3.6.0 #5608 by @renovate
  • fix(deps): update dependency cheerio to v1.0.0-rc.11 #5609 by @renovate
  • fix(deps): update dependency msw to v0.40.2 #5610 by @renovate
  • chore(deps): update dependency @playwright/test to v1.22.2 #5617 by @renovate
  • fix(deps): update dependency react-hook-form to v7.31.2 #5618 by @renovate
  • fix(deps): update graphql-tools monorepo #5619 by @renovate
  • fix(deps): update dependency eslint-plugin-jest-dom to v4.0.2 #5622 by @renovate
  • fix(deps): update dependency eslint to v8.16.0 #5624 by @renovate
  • fix(deps): update dependency core-js to v3.22.6 #5626 by @renovate
  • fix(deps): update dependency @pmmmwh/react-refresh-webpack-plugin to v0.5.7 #5628 by @renovate
  • fix(deps): update dependency concurrently to v7.2.1 #5629 by @renovate
  • fix(deps): update dependency @graphql-yoga/common to v2.6.1 #5633 by @renovate
  • fix(deps): update typescript-eslint monorepo to v5.26.0 #5634 by @renovate
  • chore(deps): update dependency cypress to v9.7.0 #5635 by @renovate
  • fix(deps): update dependency @apollo/client to v3.6.5 #5636 by @renovate
  • fix(deps): update dependency core-js to v3.22.7 #5638 by @renovate
  • fix(deps): update storybook monorepo to v6.5.5 #5639 by @renovate
  • chore(deps): update dependency @auth0/auth0-spa-js to v1.22.0 #5640 by @renovate
  • chore(deps): update dependency typescript to v4.7.2 #5641 by @renovate
  • fix(deps): update dependency @graphql-yoga/common to v2.7.0 #5643 by @renovate
  • chore(deps): update dependency npm-packlist to v5.1.0 #5650 by @renovate
  • chore(deps): update dependency esbuild to v0.14.40 #5653 by @renovate
  • chore(deps): update dependency esbuild to v0.14.42 #5654 by @renovate
  • fix(deps): update docusaurus monorepo to v2.0.0-beta.21 #5656 by @renovate
  • chore(deps): update dependency @types/prettier to v2.6.3 #5664 by @renovate
  • chore(deps): update dependency firebase to v9.8.2 #5665 by @renovate
  • fix(deps): update dependency react-hook-form to v7.31.3 #5666 by @renovate
  • chore(deps): update dependency @nhost/hasura-auth-js to v1.1.11 #5669 by @renovate
  • fix(deps): update dependency @apollo/client to v3.6.6 #5671 by @renovate
  • fix(deps): update dependency @types/node to v16.11.38 #5674 by @renovate
  • fix(deps): update dependency systeminformation to v5.11.16 #5676 by @renovate
  • chore(deps): update dependency @nhost/hasura-auth-js to v1.1.12 #5677 by @renovate
  • fix(deps): update dependency vscode-languageserver-textdocument to v1.0.5 #5679 by @renovate
  • fix(deps): update dependency @types/jest to v27.5.2 #5681 by @renovate
  • fix(deps): update dependency core-js to v3.22.8 #5683 by @renovate
  • fix(deps): update dependency webpack-dev-server to v4.9.1 #5684 by @renovate
  • chore(deps): update dependency @nhost/hasura-auth-js to v1.1.14 #5688 by @renovate
  • chore(deps): update dependency @envelop/testing to v4.3.3 #5690 by @renovate
  • chore(deps): update dependency @clerk/clerk-sdk-node to v3.6.1 #5697 by @renovate
  • chore(deps): update dependency typescript to v4.7.3 #5707 by @renovate
  • fix(deps): update dependency @envelop/disable-introspection to v3.3.3 #5710 by @renovate
  • chore(deps): update dependency esbuild to v0.14.43 #5717 by @renovate

Need help or having trouble upgrading?

See this forum topic for manual upgrade instructions and general upgrade help.


2.0 is super huge! (and I’m glad you’re using major version numbers for breaking changes)

would you consider including an explanation of what ‘baremetal’ means, where it’s used, and why it’s used? (or links to explanations)

for instance, I would like to deploy to EKS – is that baremetal?



Check out the docs: Introduction to Baremetal | RedwoodJS Docs

Baremetal just means the raw server, no other intermediary like Netlify or someone doing the deploy and serving the files for you: you run a web server (either yarn rw serve or a standalone web server like nginx) and internet traffic goes directly to that server! Even more scalable with a load balancer out front, but the Baremetal deploy doesn’t know anything about those. Yet. :smiling_imp:

1 Like

Ah, ok - what I used to do on linux - nice! But I’m now a docker guy. Friday Treat: GitHub - gitpod-io/template-docker-compose: A Docker Compose template, configured for Gitpod ( to give you pre-built, ephemeral development environments in the cloud. :cowboy_hat_face:

Hi! Do you have any information about SSR?

Ran the upgrade from 1.5.1 to 2.0.0 as well as lint --fix. All seems right in the world so far. Thanks for the hard work team Redwood!


It’s in the works and @danny is the project lead.

If you have the time available, it would be very helpful to know what your specific needs/use case would be — could you post in a new forum topic and loop in Danny?

Also, you might be interested in this incoming PR to add prerendering to Redwood Cells (aka dynamic SSG for pages with Cells):

1 Like

Applied the upgrade from 1.5.1 to 2.0.0-rc, ran lint --fix. No issues. Nice!