293 lines
11 KiB
Plaintext
293 lines
11 KiB
Plaintext
---
|
|
title: Production Deployment
|
|
label: Deployment
|
|
order: 10
|
|
desc: When your Payload based app is ready, tested, looking great, it is time to deploy. Learn how to deploy your app and what to consider before deployment.
|
|
keywords: deployment, production, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
|
---
|
|
|
|
<Banner type="success">
|
|
So you've developed a Payload app, it's fully tested, and running great locally. Now it's time to
|
|
launch. <strong>Awesome! Great work!</strong> Now, what's next?
|
|
</Banner>
|
|
|
|
There are many ways to deploy Payload to a production environment. When evaluating how you will deploy Payload, you need
|
|
to consider these main aspects:
|
|
|
|
1. [Basics](#basics)
|
|
1. [Security](#security)
|
|
1. [Your MongoDB](#mongodb)
|
|
1. [Permanent File Storage](#file-storage)
|
|
1. [Docker](#docker)
|
|
|
|
## Basics
|
|
|
|
In order for Payload to run, it requires both the server code and the built admin panel. These will be the `dist`
|
|
and `build` directories by default. If you've used `create-payload-app` to create your project, executing the `build`
|
|
npm script will build both and output these directories.
|
|
|
|
## Security
|
|
|
|
Payload features a suite of security features that you can rely on to strengthen your application's security. When
|
|
deploying to Production, it's a good idea to double-check that you are making proper use of each of them.
|
|
|
|
### The Secret Key
|
|
|
|
When you initialize Payload, you provide it with a `secret` property. This property should be impossible to guess and
|
|
extremely difficult for brute-force attacks to crack. Make sure your Production `secret` is a long, complex string. It's
|
|
often best practice to store it in an `env` file which is not checked into your Git repository, using `dotenv` to supply
|
|
it to your `payload.init` call.
|
|
|
|
### Double-check and thoroughly test all Access Control
|
|
|
|
Because _**you**_ are in complete control of who can do what with your data, you should double and triple-check that you
|
|
wield that power responsibly before deploying to Production.
|
|
|
|
<Banner type="error">
|
|
<strong>
|
|
By default, all Access Control functions require that a user is successfully logged in to
|
|
Payload to create, read, update, or delete data.
|
|
</strong>
|
|
|
|
But, if you allow public user registration, for example, you will want to make sure that your
|
|
access control functions are more strict - permitting <strong>only appropriate users</strong> to
|
|
perform appropriate actions.
|
|
</Banner>
|
|
|
|
### Building the Admin panel
|
|
|
|
Before running in Production, you need to have built a production-ready copy of the Payload Admin panel. To do this,
|
|
Payload provides the `build` NPM script. You can use it by adding a `script` to your `package.json` file like this:
|
|
|
|
`package.json`:
|
|
|
|
```json
|
|
{
|
|
"name": "project-name-here",
|
|
"scripts": {
|
|
"build": "payload build"
|
|
},
|
|
"dependencies": {
|
|
// your dependencies
|
|
}
|
|
}
|
|
```
|
|
|
|
Then, to build Payload, you would run `npm run build` in your project folder. A production-ready Payload Admin bundle will be
|
|
created in the `build` directory.
|
|
|
|
### Setting Node to Production
|
|
|
|
Make sure you set the environment variable `NODE_ENV` to `production`. Based on this variable, many Node packages
|
|
automatically optimize themselves. In production, Payload automatically disables
|
|
the [GraphQL Playground](/docs/graphql/overview#graphql-playground), serves a production-ready version of the Admin
|
|
panel, and other changes.
|
|
|
|
### Secure Cookie Settings
|
|
|
|
You should be using an SSL certificate for production Payload instances, which means you
|
|
can [enable secure cookies](/docs/authentication/config) in your Authentication-enabled Collection configs.
|
|
|
|
### Preventing API Abuse
|
|
|
|
Payload comes with a robust set of built-in anti-abuse measures, such as locking out users after X amount of failed
|
|
login attempts, request rate limiting, GraphQL query complexity limits, max `depth` settings, and
|
|
more. [Click here to learn more](/docs/production/preventing-abuse).
|
|
|
|
## MongoDB
|
|
|
|
Payload can be used with any MongoDB compatible database including AWS DocumentDB or Azure Cosmos DB.
|
|
|
|
### Managing MongoDB yourself
|
|
|
|
If you are using a [persistent filesystem-based cloud host](#persistent-vs-ephemeral-filesystems) such as
|
|
a [DigitalOcean Droplet](https://www.digitalocean.com/products/droplets/) or
|
|
an [Amazon EC2](https://aws.amazon.com/ec2/?ec2-whats-new.sort-by=item.additionalFields.postDateTime&ec2-whats-new.sort-order=desc)
|
|
server, you might opt to install MongoDB directly on that server itself so that Node can communicate with it locally.
|
|
With this approach, you can benefit from faster response times, but scaling can become more involved as your app's user
|
|
base grows.
|
|
|
|
### Letting someone else do it
|
|
|
|
Alternatively, you can rely on a third-party MongoDB host such as [MongoDB Atlas](https://www.mongodb.com/). With Atlas
|
|
or a similar cloud provider, you can trust them to take care of your database's availability, security, redundancy, and
|
|
backups.
|
|
|
|
<Banner type="warning">
|
|
<strong>Note:</strong>
|
|
<br />
|
|
If versions are enabled and a collection has many documents you may need a minimum of an m10
|
|
mongoDB atlas cluster if you reach a sorting `exceeded memory limit` error to view a collection
|
|
list in the admin UI. The limitations of the m2 and m5 tier clusters are here: [Atlas M0 (Free
|
|
Cluster), M2, and M5
|
|
Limitations](https://www.mongodb.com/docs/atlas/reference/free-shared-limitations/?_ga=2.176267877.1329169847.1677683154-860992573.1647438381#operational-limitations).
|
|
</Banner>
|
|
|
|
### DocumentDB
|
|
|
|
When using AWS DocumentDB, you will need to configure connection options for authentication in the `connectOptions`
|
|
passed to the `mongooseAdapter` . You also need to set `connectOptions.useFacet` to `false` to disable use of the
|
|
unsupported `$facet` aggregation.
|
|
|
|
### CosmosDB
|
|
|
|
When using Azure Cosmos DB, an index is needed for any field you may want to sort on. To add the sort index for all
|
|
fields that may be sorted in the admin UI use the <a href="/docs/configuration/overview">indexSortableFields</a>
|
|
configuration option.
|
|
|
|
## File storage
|
|
|
|
If you are using Payload to [manage file uploads](/docs/upload/overview), you need to consider where your uploaded files
|
|
will be permanently stored. If you do not use Payload for file uploads, then this section does not impact your app
|
|
whatsoever.
|
|
|
|
### Persistent vs Ephemeral Filesystems
|
|
|
|
Some cloud app hosts such as [Heroku](https://heroku.com) use `ephemeral` file systems, which means that any files
|
|
uploaded to your server only last until the server restarts or shuts down. Heroku and similar providers schedule
|
|
restarts and shutdowns without your control, meaning your uploads will accidentally disappear without any way to get
|
|
them back.
|
|
|
|
Alternatively, persistent filesystems will never delete your files and can be trusted to reliably host uploads
|
|
perpetually.
|
|
|
|
**Popular cloud providers with ephemeral filesystems:**
|
|
|
|
- Heroku
|
|
- DigitalOcean Apps
|
|
|
|
**Popular cloud providers with persistent filesystems:**
|
|
|
|
- DigitalOcean Droplets
|
|
- Amazon EC2
|
|
- GoDaddy
|
|
- Many other more traditional web hosts
|
|
|
|
<Banner type="error">
|
|
<strong>Warning:</strong>
|
|
<br />
|
|
If you rely on Payload's <strong>Upload</strong> functionality, make sure you either use a host
|
|
with a persistent filesystem or have an integration with a third-party file host like Amazon S3.
|
|
</Banner>
|
|
|
|
### Using ephemeral filesystem providers like Heroku
|
|
|
|
If you don't use Payload's `upload` functionality, you can go ahead and use Heroku or similar platform easily.
|
|
Everything will work exactly as you want it to.
|
|
|
|
But, if you do, and you still want to use an ephemeral filesystem provider, you can write a hook-based solution to
|
|
_copy_ the files your users upload to a more permanent storage solution like Amazon S3 or DigitalOcean Spaces.
|
|
|
|
**To automatically send uploaded files to S3 or similar, you could:**
|
|
|
|
- Write an asynchronous `beforeChange` hook for all Collections that support Uploads, which takes any uploaded `file`
|
|
from the Express `req` and sends it to an S3 bucket
|
|
- Write an `afterRead` hook to save a `s3URL` field that automatically takes the `filename` stored and formats a full S3
|
|
URL
|
|
- Write an `afterDelete` hook that automatically deletes files from the S3 bucket
|
|
|
|
With the above configuration, deploying to Heroku or similar becomes no problem.
|
|
|
|
## DigitalOcean Tutorials
|
|
|
|
DigitalOcean provides extremely helpful documentation that can walk you through the entire process of creating a
|
|
production-ready Droplet to host your Payload app:
|
|
|
|
1. Create a new Ubuntu 20.04 droplet on [DigitalOcean](https://digitalocean.com)
|
|
1. [Initial server setup](https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-20-04)
|
|
1. [Install nginx](https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-20-04)
|
|
1. [Install and secure MongoDB](https://www.digitalocean.com/community/tutorials/how-to-install-mongodb-on-ubuntu-20-04)
|
|
1. [Create a new MongoDB and user](https://medium.com/@mhagemann/how-to-add-a-new-user-to-a-mongodb-database-d896776b5362)
|
|
1. [Set up Node for production](https://www.digitalocean.com/community/tutorials/how-to-set-up-a-node-js-application-for-production-on-ubuntu-20-04)
|
|
|
|
### Swap Space
|
|
|
|
Swap refers to a section of storage on the hard drive that is reserved to temporarily store data that can no longer fit
|
|
within RAM. This allows for the expansion of your server's working memory, with some limitations. Swap space comes into
|
|
play when available RAM can no longer accommodate actively used application data, enabling the system to continue
|
|
functioning.
|
|
|
|
Insufficient space can lead to deployment errors and memory-related issues, resulting in application crashes, sluggish
|
|
performance, or an unresponsive server.
|
|
|
|
Common deployment error due to **space limitations** (as reported by users):
|
|
|
|
- `Error: Command failed with exit code 1`
|
|
|
|
To configure swap, we recommend following this tutorial
|
|
on [How To Add Swap Space](https://www.digitalocean.com/community/tutorials/how-to-add-swap-space-on-ubuntu-22-04).
|
|
|
|
## Docker
|
|
|
|
This is an example of a multi-stage docker build of Payload for production. Ensure you are setting your environment
|
|
variables on deployment, like `PAYLOAD_SECRET`, `PAYLOAD_CONFIG_PATH`, and `DATABASE_URI` if needed.
|
|
|
|
```dockerfile
|
|
FROM node:18-alpine as base
|
|
|
|
FROM base as builder
|
|
|
|
WORKDIR /home/node
|
|
COPY package*.json ./
|
|
|
|
COPY . .
|
|
RUN yarn install
|
|
RUN yarn build
|
|
|
|
FROM base as runtime
|
|
|
|
ENV NODE_ENV=production
|
|
|
|
WORKDIR /home/node
|
|
COPY package*.json ./
|
|
|
|
RUN yarn install --production
|
|
COPY --from=builder /home/node/dist ./dist
|
|
COPY --from=builder /home/node/build ./build
|
|
|
|
EXPOSE 3000
|
|
|
|
CMD ["node", "dist/server.js"]
|
|
```
|
|
|
|
## Docker Compose
|
|
|
|
Here is an example of a docker-compose.yml file that can be used for development
|
|
|
|
```yml
|
|
version: '3'
|
|
|
|
services:
|
|
payload:
|
|
image: node:18-alpine
|
|
ports:
|
|
- '3000:3000'
|
|
volumes:
|
|
- .:/home/node/app
|
|
- node_modules:/home/node/app/node_modules
|
|
working_dir: /home/node/app/
|
|
command: sh -c "yarn install && yarn dev"
|
|
depends_on:
|
|
- mongo
|
|
environment:
|
|
DATABASE_URI: mongodb://mongo:27017/payload
|
|
PORT: 3000
|
|
NODE_ENV: development
|
|
PAYLOAD_SECRET: TESTING
|
|
|
|
mongo:
|
|
image: mongo:latest
|
|
ports:
|
|
- '27017:27017'
|
|
command:
|
|
- --storageEngine=wiredTiger
|
|
volumes:
|
|
- data:/data/db
|
|
logging:
|
|
driver: none
|
|
|
|
volumes:
|
|
data:
|
|
node_modules:
|
|
```
|