DEV Community

Cover image for Migrate Strapi from Render to Fly.io
Cândido Sales Gomes
Cândido Sales Gomes

Posted on • Edited on

Migrate Strapi from Render to Fly.io

I was having cost issues with the Render server, and I can't reduce the server for small machines. Most of my users are from Brazil, so I moved my Strapi to Fly.io, which enables reduced costs.

Fly.io better manages your VM using Firecracker, and I can choose servers located in Brazil (Guarulhos—Sao Paulo). It will provide a better experience for my users and better maintainability.

In this article, you will see all my steps to accomplish this migration.

My previous infrastructure was:

Render (Cost US$60.25/month)

Service Memory Disk CPU
PostgreSQL 1 GB RAM 16 GB (using only 96 MB) 1 CPU
Strapi (Node) 2 GB RAM (using only 600MB) 1 GB (using only 0.28GB) 1 CPU
Meilisearch 1 GB RAM (using only 60MB) 1 CPU

Now ...

Fly.io (Cost US$9.16/month)

Service Memory Disk CPU
PostgreSQL 256 MB RAM 1 GB 1 (shared)
Strapi (Node) 1 GB RAM 1 GB 1 (shared)
Meilisearch 1 GB RAM 1 (shared)

Deploy Strapi in Fly.io

Install Fly CLI

brew install flyctl
Enter fullscreen mode Exit fullscreen mode

Login to your Fly account.

Follow the instructions and return to the command line.

fly auth login
Enter fullscreen mode Exit fullscreen mode

Create the fly.toml, dockerfile and .dockerignore in your current project

fly.toml

# fly.toml app configuration file generated for app-name on 2024-07-29T15:46:53-04:00
#
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
#

app = 'app-name'
primary_region = 'gru'
kill_timeout = '5m0s'

[build]

[[mounts]]
  source = 'uploads'
  destination = '/public/uploads'
  initial_size = '1gb'

[http_service]
  internal_port = 1337
  force_https = true
  auto_stop_machines = 'stop'
  auto_start_machines = true
  min_machines_running = 0
  processes = ['app']

[[services]]
  protocol = 'tcp'
  internal_port = 1337
  processes = ['app']

  [[services.ports]]
    port = 80
    handlers = ['http']

[[vm]]
  cpu_kind = 'shared'
  cpus = 1
  memory_mb = 1024

Enter fullscreen mode Exit fullscreen mode

Multi-stage dockerfile using Alpine (it will reduce the image 4.3GB → 1.1GB)

dockerfile

FROM node:18-alpine3.18 AS build_image

# Installing libvips-dev for sharp Compatability
RUN apk update && apk add --upgrade --no-cache vips-dev build-base fftw-dev gcc autoconf g++ make libc6-compat zlib-dev libpng-dev nasm bash --repository https://alpine.global.ssl.fastly.net/alpine/v3.18/community/

# Set working directory
WORKDIR /opt

# Resolve node_modules for caching
COPY package.json yarn.lock ./

# Install dependencies
RUN yarn install

# Copy all for build and release cache if package.json update
COPY . .

ENV NODE_ENV=production
RUN yarn build

# Remove unnecessary files from node_modules - https://github.com/tj/node-prune
RUN wget https://gobinaries.com/tj/node-prune --output-document - | /bin/sh && node-prune

#------------------------------------------------------------------------------------

# Create a new namespace for the final Docker Image
FROM node:18-alpine3.18

# Installing openssh 
RUN apk --no-cache update && apk add openssh

# Installing vips-dev (https://www.libvips.org/) - https://github.com/strapi-community/strapi-plugin-local-image-sharp
RUN apk add vips-dev

# Set working directory
WORKDIR /opt/app

# Only copy your source code without the system file
COPY --from=build_image /opt ./

EXPOSE 1337

ENV NODE_ENV=production
ENV STRAPI_LOG_LEVEL=debug

CMD ["yarn", "start"]
Enter fullscreen mode Exit fullscreen mode

.dockerignore

.tmp/
.cache/
.git/
build/
node_modules/
data/
Enter fullscreen mode Exit fullscreen mode

Before launch, please copy and paste all environment variables in the .env file to make it easier to deploy

fly launch
Enter fullscreen mode Exit fullscreen mode

Wait until the deployment finishes.

Image description

Strapi data transfer

Strapi provides an API to transfer data (schema, content, etc.) between servers. It’s pretty handy! Thanks, Strapi team!

First, we have to access your Strapi Admin in the Fly.io and generate a token that enables PUSH data

Image description

Create the token with PUSH permission.

Image description

Save the token in a safe place.

Access your Render server and run the following command in the root folder of the Strapi project (~/project/src). This command will send all data related to the database (entities and links) to your Fly.io server. It’s amazing! We will send the assets from the public folder later.
1. Unfortunately, I didn’t find a way to send the files (assets) because I received this error: [FATAL] restore failed Error: The backup folder for the assets could not be created inside the public folder. Please ensure Strapi has write permissions on the public directory. This issue is caused by the folder linked to the volume not having permission to run some strapi scripts into it.

npm run strapi transfer -- --to https://app-name.fly.dev/admin --exclude files
Enter fullscreen mode Exit fullscreen mode

Now paste the transfer token when you are asked.

Run the strapi transfer data command

Waiting until finish the transfer.

Final transfer Strapi data

Later you can check in the Fly.io if the content is there.

Next, transfer all environment variables of .env file for Secrets in Fly.io

Environment secrets

Now we going to send the files (images)

Backup Render public folder

Access the server via SSH. How to set up your SSH connection.

Image description

ssh YOUR_SERVICE@ssh.YOUR_REGION.render.com
Enter fullscreen mode Exit fullscreen mode

Compress the public folder.

zip -r public.zip public
Enter fullscreen mode Exit fullscreen mode

Close the SSH connection.

exit
Enter fullscreen mode Exit fullscreen mode

You can transfer files using SCP. Copying a file from your service to your local machine

scp -s YOUR_SERVICE@ssh.YOUR_REGION.render.com:/path/to/remote/file /destination/path/for/local/file

scp -s srv-cbtddcr19n0c5dqs15i0@ssh.oregon.render.com:/opt/render/project/src/public.zip ./strapi/public.zip
Enter fullscreen mode Exit fullscreen mode

Next, we must create a folder called public_in your strapi root folder (locally). It will serve as a temporary folder to transfer the backup content. Now unzip the public.zip into the folder.

mkdir public_

# Unzip, after creating a public folder into the public_ 
# We must move the content of the public_/public for public_
unzip public.zip -d public_

# Enter into the folder, move the content and delete the unnecessary folder
cd public_ && mv public/* . && rm -rf public

# It should contain only robots.txt and uploads folder
ls
Enter fullscreen mode Exit fullscreen mode

Now your project contains two public folders (one is public_ is a temp folder).

Now run the deploy.

fly deploy
Enter fullscreen mode Exit fullscreen mode

During the deployment, all folders from the project will be copied into the docker image so you can later access the VM and move the content.

Wait until the deployment finishes.

Now access via SSH the VM.

fly ssh console
Enter fullscreen mode Exit fullscreen mode

Inside the VM, run the command below.

cp -r /opt/app/public_/. /opt/app/public
rm -rf /opt/app/public_
Enter fullscreen mode Exit fullscreen mode

It will copy the content from public_to publicfolder and later delete the public_.

I know it’s pretty verbose and complex, but we have permission to perform this kind of operation only via SSH.

💡 I hope the Fly.io team will enable us to send files for our volumes without running a machine using sftp. I tried other alternatives using rsync and wireguard, which are the worst.

Check if the volume of the machine was increased (the default is 70MB). This is an indication that persisted in volume.

Image description


I hope this tutorial can help you migrate without pain.

Fly.io is one of the best money-saving options for anyone starting a small project. I no longer had any problems connecting to the bank; even with the reduction of infrastructure, there was no loss in user experience.

If you have any questions, please feel free to comment below.

References

Top comments (1)

Collapse
 
lansolo99 profile image
Stéphane CHANGARNIER

Thanks for the recap; it has been of great help!

However, my use case was connecting a local Strapi instance to a remote one hosted on Fly.io. I had to handle proxying the remote database to establish a connection.

For some reason, I couldn’t use port 5432 and had to use 5433 instead:
flyctl proxy 5433 -a

Additionally, I used the wrong generated Strapi token and encountered unauthorized connection issues because I used the source Strapi one instead of the remote Strapi one 🙃.

Finally, I had to deal with a 'duplicate key value' error during the operation, which prevented a successful transfer. I had to implement some skipping logic to complete the entire migration.