This is a step-by-step guide for deploying a Strapi project on, using the Postgres on Fly integration as persistent storage.

In this guide you are going to:

  • Create a Strapi project
  • Create a Fly project
  • Setup and deploy a Postgres app on Fly
  • Deploy the Strapi project on Fly
  • Change the Strapi project and re-deploy on Fly


To follow this guide, you will need an existing account. For the first sign-up, Fly will require you to register your credit card. This guide will use only free-tier features of Fly, so your card is not going to be charged by following this.

Install the Fly CLI

Download and install the Fly CLI for your operating system. For more configuration and information, follow Introducing Flyctl • Fly Docs.

brew install flyctl


curl -L | sh


curl -L | sh

The flyctl installation will create the symlink to fly, so in terminal both flyctl and fly are interchangeable.

Login to Fly

Login to your Fly account. Follow the instructions and return to the command line.

fly auth login

Create the Strapi project

Generate the project

Create a new Strapi project using create-strapi-app.


npx create-strapi-app@latest strapi-fly --quickstart


yarn create strapi-app strapi-fly --quickstart

When you use --quickstart to create a Strapi project, a SQLite database will be used locally in the development environment. We will configure the production environment to use Postgres later in this guide.

Create the Dockerfile

Fly uses Dockerfiles to deploy its projects. Since Strapi doesn’t generate a Dockerfile for the project, you will have to create it. Create a new file Dockerfile in the root of your project.


# Dockerfile
FROM node:16
# Installing libvips-dev for sharp Compatability
RUN apt-get update && apt-get install libvips-dev -y
# Set environment to production
ENV NODE_ENV=production
# Copy the configuration files
COPY ./package.json ./yarn.lock ./
ENV PATH /opt/node_modules/.bin:$PATH
# Install dependencies
RUN yarn install
# Copy the application files
WORKDIR /opt/app
COPY ./ .
# Build the Strapi application
RUN yarn build
# Expose the Strapi port
# Start the Strapi application 
CMD ["yarn", "start"]


# Dockerfile
FROM node:16
# Installing libvips-dev for sharp Compatability
RUN apt-get update && apt-get install libvips-dev -y
# Set environment to production
ENV NODE_ENV=production
# Copy the configuration files
COPY ./package.json ./package-lock.json ./
ENV PATH /opt/node_modules/.bin:$PATH
# Install dependencies
RUN npm install
# Copy the application files
WORKDIR /opt/app
COPY ./ .
# Build the Strapi application
RUN yarn build
# Expose the Strapi port
# Start the Strapi application
CMD ["npm", "start"]

Simen Daehlin wrote a full guide on using Strapi with Docker at Docker with Strapi V4.

Create the .dockerignore file

One the root of your project, create another file called .dockerignore. This will indicate what files should be ignored during the Docker build.

# .dockerignore

Create the Fly project

Time to create a new Fly project inside of the Strapi root folder.

fly launch

Follow the instructions on the screen, until the Would you like to setup a Postgresql database now? is prompted.

? App Name (leave blank to use an auto-generated name): 
Automatically selected personal organization
? Select region: ams (Amsterdam, Netherlands)
Created app bold-haze-733 in organization personal
Wrote config file fly.toml

Setup the Postgres project

You will be prompted Would you like to setup a Postgresql database now?. If you select Yes, Fly will create a separate Postgres project and attach it to your Strapi project. If you have a separate Postgres instance or you want to configure it later, select No and read further Postgres on Fly • Fly Docs.

Once you select Yes and select your preferred configuration, the Postgres app is being deployed.

? Would you like to setup a Postgresql database now? Yes
For pricing information visit:
? Select configuration: Development - Single node, 1x shared CPU, 256MB RAM, 1GB disk
Creating postgres cluster bold-haze-733-db in organization personal
Postgres cluster bold-haze-733-db created

Explore how you can use the free resources on Fly at About Free Postgres on Fly • Fly Docs

Once the Postgres cluster is deployed, you will be prompted with the configuration details. Make sure to save these, since you won’t be able to revisit them.

  Username:    postgres
  Password:    9904f659e488d9bb0572491ef3693c8c5df4345b8e136aed
  Hostname:    bold-haze-733-db.internal
  Proxy Port:  5432
  PG Port: 5433
Save your credentials in a secure place, you won't be able to see them again!

Moreover, Fly will provide you with the credentials that can be used internally in your app. Read more about connecting to Postgres within Fly vs. outside Fly in Connecting to Postgres • Fly Docs.

Attach Postgres to the Strapi app

Fly will automatically attach the Postgres cluster to the Strapi that you just created.

Postgres cluster bold-haze-733-db is now attached to bold-haze-733
The following secret was added to bold-haze-733:
Postgres cluster bold-haze-733-db is now attached to bold-haze-733

Fly automatically adds the DATABASE_URL in the environment secrets of the app. The Fly secrets are available to the app when it’s running, but to deploy it from your machine, you need to make it available locally first. Edit the .env file of your Strapi project to include the exact same variable.

# .env

If you connect to your Strapi database externally, read more about managing Postgres or connecting from outside Fly at Postgres on Fly • Fly Docs.

When prompted Would you like to deploy now?, select No. You need to configure the production environment first.

Configure the production environment

Before deploying the Strapi app, you will need to configure it to be production-ready.

Configure Fly

The Fly CLI generated the configuration file fly.toml. This file should point the port that Strapi is using. Go to the fly.toml file and change the internal_port row (line 17) to 1337.

  http_checks = []
  internal_port = 1337
  processes = ["app"]
  protocol = "tcp"
  script_checks = []

Configure Strapi

Because you are using Postgres, you will need to install the pg-connection-string package to split DATABASE_URL and pg as a client.


yarn add pg-connection-string pg


npm i pg-connection-string pg

Inside of the ./config directory, you need to setup a database config file for the production environment. Create a new file ./config/env/production/database.js. This will connect Strapi to the production Postgres database you just created.

// ./config/env/production/database.js
const parse = require("pg-connection-string").parse;
const config = parse(process.env.DATABASE_URL);
module.exports = () => ({
  connection: {
    client: "postgres",
    connection: {
      port: config.port,
      database: config.database,
      user: config.user,
      password: config.password,
      ssl: false,
    debug: false,

This will configure the production environment to use Postgres. The local development config will remain SQLite and can be found in ./config/database.js. If you want to configure it, read more about Database configuration - Strapi Developer Docs.

Deploy to fly

Time to deploy your Strapi application to Fly.

fly deploy

This will build the Dockerfile with your project and deploy it to Fly. Follow the URL generated by Fly to get to your admin dashboard.

From here, continue using Strapi as you would normally do, or follow the Quick Start Guide - Strapi Developer Docs.

Strapi does not allow you to use the Content-type Builder in production. To update the content types, you will have to make the changes locally and re-deploy them to Fly.

Update the Strapi project

Even if you deployed your Strapi app to production, you can still use your local environment as development environment. In order to make the local changes available to production you will need to re-deploy the Fly app.

Following Quick Start Guide - Strapi Developer Docs, start by running Strapi in development mode.


yarn develop  


npm run develop

After you make the changes and you are sure they work, re-deploy your app.

fly deploy

Hi Shaun,

Thank you so much for writing the guide! I was able to get it working. But I have a question regarding the disk requirement. The documentation mentions that it needs at least 32 GB of disk space. Is this for the database? Or is this expected to be for the directory where the strapi app is running?

In my use case I’m using the r3 plugin for the Media Library and I configured the db as you described in the article.

Thanks a lot!

1 Like

I think this is just the disk space for Strapi, but I am not exactly certain.

this is perfect, thanks a lot!

Works like a charm, your are a life saver!

Hi All, Just to clarify I only moved the guide from our GitHub repository to the Forum and made some formatting changes. GitHub user BogDAAAMN wrote the guide :bowing_man:.

May you have fair (deployment) winds and following seas!

Thanks a lot. Could deploy.
Unfortanetely somewhere I wrote wrong the url-s or I do not know.
I get 500 at the deployed version, Any idea?:
FetchError: request to* failed, reason: connect ECONNREFUSED

If someone needs to upload files, and doesn’t want to use S3 or other provider. You can create Volumen on fly io and upload files on persistent storage.

First you need to add this line in your fly.toml


Then in plugins.js add (this is explained in docs)

upload: {
    config: {
      providerOptions: {
        localServer: {
          maxage: 300000,

then you can run

fly deploy

once more

This took me some time to figure out so I hope this will help someone

Hey, I have an error during the fly deployment. Any idea ?

==> Creating build context
--> Creating build context done
==> Building image with Docker
--> docker host: 20.10.12 linux x86_64
[+] Building 44.7s (0/1)                                                                                 
[+] Building 9.1s (7/11)                                                                                 
 => [internal] load remote build context                                                            0.0s
 => copy /context /                                                                                 5.4s
 => [internal] load metadata for                                          0.7s
 => [1/8] FROM  0.0s
 => CACHED [2/8] RUN apt-get update && apt-get install libvips-dev -y                               0.0s
 => CACHED [3/8] WORKDIR /opt/                                                                      0.0s
 => ERROR [4/8] COPY ./package.json ./yarn.lock                                                     0.0s
 > [4/8] COPY ./package.json ./yarn.lock:
Error failed to fetch an image or build from source: error building: failed to compute cache key: "/package.json" not found: not found

Nobody can help ?

paste contents of Dockerfile

When i flyctl deploy my app,
It builds correctly. On the monitoring page, everything seems fine:

Welcome back! To manage your project :rocket:, go to the administration
panel at: To access the server :zap:, go to:

But when I access my app in the browser, nothing shows up and I get a ERR_CONNECTION_RESET error.

I’ve read a post [Outage?] “Connection reset by peer” when using non-default ports which explains that will no more support non-default (80/443) ports for free, it will be charged 2$/month.

Could this be the cause of my problem ?

I’ve tested to manually change the Strapi port to 80; and it seems to work like this:

  • I’ve set PORT=80 in my .env file

  • Indeed, I also had to update fly.toml

       internal_port = 80
    #also I added those
      port = 80
      handlers = ["http"]
      force_https = true
      port = 443
      handlers = ["tls", "http"]
  • and Dockerfile

     # Expose the Strapi port
     EXPOSE 80

Is this OK ? Is there a specific reason for Strapi to use port 1337 ?

Also, there’s an error in your NPM Dockerfile:

# Build the Strapi application
RUN yarn build

should be (I guess)

# Build the Strapi application
RUN npm run build

Maybe this guide needs an update ?

  • also, I’ve added the port 80 as a fly secret to override the .env / default value.
fly secrets set PORT=80

I think there’s a typo in .dockerignore (or that strapi doesn’t use the same file structure anymore)


should be


instead ?
Since I ignored the public dir (which has images), my build time is 300 s faster.