These integration guides are not official documentation and the Strapi Support Team will not provide assistance with them.
This is a step-by-step guide for deploying a Strapi project to Azure using Platform-as-a-Service (PaaS). If you're interested in using Infrastructure-as-a-Service (IaaS) refer to IaaS deployment guide.
There are 3 ways which you can deploy the Azure resources:
When Strapi is running in a PaaS hosting model, a custom storage provider will be required to avoid the transient disk of the PaaS model, which is covered towards the end.
There are three resources in Azure that are required to run Strapi in a PaaS model, AppService to host the Strapi web application, Storage to store images/uploaded assets, and a database, Azure has managed MySQL and Postgres to choose from (for this tutorial, we'll use MySQL, but the steps are the same for Postgres).
In this section we'll use the Azure Portal to create the required resources to host Strapi.
Navigate to the Azure Portal
Click Create a resource and search for Resource group from the provided search box.
Provide a name for your Resource Group, my-strapi-app
, and select a region.
Click Review + create then Create.
Navigate to the Resource Group once it's created, click Create resources. and search for Web App
Ensure the Subscription and Resource Group are correct, then provide the following configuration for the app:
my-strapi-app
Code
Node 16 LTS
Linux
Use the App Service Plan to select the appropriate Sku and size for the level of scale your app will need (refer to the Azure docs for more information on the various Sku and sizes).
Click Review + create then Create.
Navigate back to the Resource Group and click Create then search for Storage account and click Create.
Ensure the Subscription and Resource Group are correct, then provide the following configuration for the storage account:
my-strapi-app
Standard
Click Review + create then Create
Navigate back to the Resource Group and click Create then search for Azure Database for MySQL and click Create.
Select Single server for the service type.
Ensure the Subscription and Resource Group are correct, then provide the following configuration for the storage account:
my-strapi-db
None
(unless you're wanting to import from a backup)5.7
Enter a username and password for the Administrator account, click Review + create then Create.
Once all the resources are created, you will need to get the connection information for the MySQL and Storage account to the Web App, as well as configure the resources for use.
strapi-uploads
, and set Public access level to Blob
, then click Create.Allow access to Azure services
to Yes
and click Save.Open the Azure Cloud Shell and log into the mysql
CLI:
mysql --host <server> --user <username> -p
Create a database for Strapi to use CREATE DATABASE strapi;
then close the Cloud Shell.
In this section, we'll use the Azure CLI to create the required resources. This will assume you have some familiarity with the Azure CLI and how to find the right values.
Create a new Resource Group.
rgName=my-strapi-app
location=westus
az group create --name $rgName --location $location
Create a new Linux App Service Plan (ensure you change the number-of-workers
and sku
to meet your scale requirements).
appPlanName=strapi-app-service-plan
az appservice plan create --resource-group $rgName --name $appPlanName --is-linux --number-of-workers 4 --sku S1 --location $location
Create a Web App running Node.js 16.
webAppName=my-strapi-app
az webapp create --resource-group $rgName --name $webAppName --plan $appPlanName --runtime "node|16-lts"
Create a Storage Account.
saName=mystrapiapp
az storage account create --resource-group $rgName --name $saName --location $location
# Get the access key
saKey=$(az storage account keys list --account-name $saName --query "[?keyName=='key1'].value" --output tsv)
# Add a container to the storage account
container=strapi-uploads
az storage container create --name $container --public-access blob --account-key $saKey --account-name $saName
Create a MySQL database.
serverName=my-strapi-db
dbName=strapi
username=strapi
password=...
# Create the server
az mysql server create --resource-group $rgName --name $serverName --location $location --admin-user $username --admin-password $password --version 5.7 --sku-name B_Gen5_1
# Create the database
az mysql db create --resource-group $rgName --name $dbName --server-name $serverName
# Allow Azure resources through the firewall
az mysql server firewall-rule create --resource-group $rgName --server-name $serverName --name AllowAllAzureIps --start-ip-address 0.0.0.0 --end-ip-address 0.0.0.0
Add configuration values to the Web App.
az webapp config appsettings set --resource-group $rgName --name $webAppName --setting STORAGE_ACCOUNT=$saName
az webapp config appsettings set --resource-group $rgName --name $webAppName --setting STORAGE_ACCOUNT_KEY=$saKey
az webapp config appsettings set --resource-group $rgName --name $webAppName --setting STORAGE_ACCOUNT_CONTAINER=$container
az webapp config appsettings set --resource-group $rgName --name $webAppName --setting DATABASE_HOST=$serverName.mysql.database.azure.com
az webapp config appsettings set --resource-group $rgName --name $webAppName --setting DATABASE_USERNAME=$username@$serverName
az webapp config appsettings set --resource-group $rgName --name $webAppName --setting DATABASE_PASSWORD=$password
To deploy using an Azure Resource Manager template, use the button below, or upload this template as a custom deployment in Azure.
Azure AppService can be deployed to using CI/CD pipelines or via FTPS, refer to the Azure docs on how to do this for your preferred manner.
As AppService is a PaaS hosting model, an upload provider will be required to save the uploaded assets to Azure Storage. Check out this community provider for more details on using Azure Storage as an upload provider.
To start the Node.js application, AppService will run the npm start
command. As there is no guarantee that the symlinks created by npm install
were preserved (in the case of an upload from a CI/CD pipeline) it is recommended that the npm start
command directly references the Strapi entry point:
1
2
3
"scripts": {
"start": "node node_modules/@strapi/strapi/bin/strapi.js start"
}
Databases can be on a Azure Virtual Machine, hosted externally as a service, or via the Azure Managed Databases.
You will want to use an Azure Virtual Machine for Strapi deployments, the Azure web-app (IIS) deployments are not recommended.
Create a resource
Virtual machines are listed under the Compute
category. You will need the following options:
Project Details:
Create new
Instance Details:
Administrator Account:
SSH public key
root
Inbound port rules:
More configuration for this will be done when we get to the Networking
tab of the VM creation for now just leave these as the defaults.
It is entirely up to you which OS Disk type you use, for the cheapest option you should select Standard HDD
with no encryption. You can also add additional disks to the virtual machine if you need additional space (such as for uploads).
For the networking configuration you will want to leave the following as defaults:
However for the NIC network security group we will want to select Advanced
and Create New
. You can name this whatever you like but for inbound rules we want to allow:
For each of the ports to allow, you will hit create new and enter the following information:
*
HTTPS_Port
or Strapi_Port
Entirely optional but you can enable the OS guest diagnostics
to configure alerting later on, everything else on this tab is also entirely optional. For now let's leave everything as the default options.
If you are familiar with standard Linux Cloud init
you are welcome to add in any configuration script to jump-start your virtual machine with some pre-configured packages, however we will leave everything here as default.
You are welcome to tag this virtual machine to easily identify it and others part of your "Project" later on.
Finally review you configuration and wait for Azure to validate the configuration, on this page you will also see the price of your VM per hour. This price will vary based on the region and size of virtual machine you selected (along with any additional options).
Once you have finished verifying your config hit the Create
button. It may take a few minutes for your deployment to complete, you will see a log of the deployment on the next page. When it's finished you will see Your deployment is complete
, simply hit Go to resource
.
These next steps will help you set up a production server and setup a non-root service account for managing your Strapi instance. You will need the administrator account you created previously and the public IP listed on your resource page.
You will use the admin user created previously as well as the SSH key you added to your Azure account. For Linux/Mac users you can use your terminal, likewise for Windows users you can use the built -in SSH tool in Powershell or use an SSH client like Putty.
ssh yourAdminUser@yourAzureVMPublicIP
# ssh strapiAdmin@10.0.0.1
After you login via SSH we need to update the container and install any packages that Strapi may need. The following packages have been identified to be required to run Strapi on Ubuntu 18.04 LTS:
First we need to update existing packages, you will use the apt package manager to do this:
sudo apt update
sudo apt upgrade -y
After the updates have installed, we will move on to installing required dependencies from the existing apt repos before we add any additional ones:
sudo apt install libpng-dev build-essential -y
For Node.js it is recommended you use the official source, per the instructions we will use the following commands:
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - &&\
sudo apt-get install -y nodejs
Likewise for Yarn we will use the instructions from the Yarn documentation:
corepack enable
yarn set version stable
yarn install
To verify you have everything installed properly you can run the following:
node -v && npm -v && yarn -v
This should print out the versions of each.
A service user is defined as a user who has limited permissions and access, this user typically does not have access to sudo
nor can modify anything else on the system.
We will create the service user with a disabled login to ensure it cannot be accessed via SSH.
sudo adduser --shell /bin/bash --disabled-login --gecos "" --quiet strapi
We will be making the /srv/strapi
directory to hold our project. From here we can either create a new project or clone an existing one from say a Github or Gitlab repository. For private repositories, you will likely need to setup additional SSH keys and access.
First we will make the directory, then we will give the strapi
service user access to read/write.
sudo mkdir /srv/strapi
sudo chown strapi:strapi /srv/strapi
Strapi will need a database in order to save data to, you have multiple options for this.
For Azure managed databases you can use the following:
Likewise you can use any of the following installed locally on the virtual machine:
In our example we will be using MariaDB 10.4 LTS using the MariaDB apt repo. Per the documentation we will use the following commands:
sudo apt-key adv --fetch-keys 'https://mariadb.org/mariadb_release_signing_key.asc'
sudo add-apt-repository 'deb [arch=amd64,arm64,ppc64el] http://sfo1.mirrors.digitalocean.com/mariadb/repo/10.4/ubuntu bionic main'
sudo apt update
sudo apt install mariadb-server-10.4 -y
Then we will need to create our Strapi database, database user, and grant the user access. We will need to enter a MySQL shell to perform these commands. MariaDB 10.4+ no longer has a root password and instead the root user is granted access via sudo
sudo mysql -u root
You should now see the following prompt MariaDB [(none)]>
, this is the MySQL shell that allows us to give commands to the MariaDB server.
# Create the database
create database strapi_dev;
# Create the database user
create user strapi@localhost identified by 'mysecurepassword';
# Grant the database user permissions
grant all privileges on strapi_dev.* to strapi@localhost;
# Flush Privileges to reload the grant tables
FLUSH PRIVILEGES;
# Exit the MySQL shell
exit
And we now have everything we need to create/start our Strapi project.
If you are cloning a project from Git your configuration steps may be different depending on how you store private information such as database information, JWT secret keys, and various other secrets. In our case we will be creating a new project.
You will first need to change users into the service user created previously, and change directories to the service directory
# Change users
sudo su strapi
# Change directory
cd /srv/strapi
Now we can use Yarn to create our project, this does not require you to install any global packages much like npx.
yarn create strapi-app mystrapiapp
This will bring you to an interactive command shell to enter the following information:
strapi_dev
127.0.0.1
3306
strapi
mysecurepassword
N
Yarn will now install the project and install all the Node.js dependencies, we can now change directory again and start the project.
# Change directory
cd mystrapiapp
# Temporary start (will build the Admin panel)
yarn develop
Now you should be able to access your Strapi application on the public IP of your virtual machine via the default Strapi port of 1337
. Naturally if we close the SSH session the Strapi server will also be killed so we need to run it as a service.
By default Strapi will predefine environment variables to use with your database config.
If we edit the ./config/database.js
file using nano
we can replace a few key settings:
nano /srv/strapi/mystrapiapp/config/database.js
Using the following example we will remove any private information:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// path: /srv/strapi/mystrapiapp/config/database.ts
export default ({ env }) => ({
connection: {
client: 'mysql',
connection: {
host: env('DB_HOST'),
port: env.int('DB_PORT'),
database: env('DB_NAME'),
user: env('DB_USER'),
password: env('DB_PASS'),
ssl: {
rejectUnauthorized: env.bool('DATABASE_SSL_SELF', false), // For self-signed certificates
},
},
debug: false,
},
});
Now we will install PM2 to run Strapi as a service, and using the PM2 ecosystem config file we can define our environment variables.
Using Yarn, we will install PM2
globally, and adjust our .bashrc
file to allow us to use global Yarn packages:
# Install PM2
yarn global add pm2
# Edit our ~/.bashrc file for global Yarn packages
nano ~/.bashrc
You will want to paste the following at the end of the `.bashrc file:
export PATH="$PATH:$(yarn global bin)"
From here you can either exit the service user and log back in for the changes to take effect or simply run:
source ~/.bashrc
And we will create our ecosystem.config.js
using the pm2 init
command, you should run this in your Strapi project directory
# Create the ecosystem file
pm2 init
You should get the message File /srv/strapi/mystrapiapp/ecosystem.config.js generated
, lets go ahead and edit this file using nano
and add our config settings.
nano /srv/strapi/mystrapiapp/ecosystem.config.js
Replace the boilerplate information with something like the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module.exports = {
apps: [
{
name: 'strapi-dev',
cwd: '/srv/strapi/mystrapiapp',
script: 'npm',
args: 'start',
env: {
NODE_ENV: 'development',
DB_HOST: 'localhost',
DB_PORT: '5432',
DB_NAME: 'strapi_dev',
DB_USER: 'strapi',
DB_PASS: 'mysecurepassword',
JWT_SECRET: 'aSecretKey',
},
},
],
};
You can also set your environment variables in a .env
file in your project like so:
DB_HOST=localhost
DB_PORT=5432
DB_NAME=strapi_dev
DB_USER=strapi
DB_PASS=mysecurepassword
JWT_SECRET=aSecretKey
We recommend you continue setting the NODE_ENV
variable in the ecosystem.config.js
file.
Then use the following command to start the Strapi service:
pm2 start ecosystem.config.js
The Strapi PM2
service is now set-up to use an ecosystem.config.js
to manage your application.
OPTIONAL: You may see your project and set-up your first administrator user, by creating an admin user.
Follow the steps below to have your app launch on system startup.
pm2 startup systemd
[PM2] Init System found: systemd
[PM2] To setup the Startup Script, copy/paste the following command:
sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u your-name --hp /home/your-name
For the following command we want to be using your virtual machine admin user not the service user, to exit from the service user you can simply run the command exit
to return to your admin user.
sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u your-name --hp /home/your-name
[PM2] Init System found: systemd
Platform systemd
. . .
[PM2] [v] Command successfully executed.
+---------------------------------------+
[PM2] Freeze a process list on reboot via:
$ pm2 save
[PM2] Remove init script via:
$ pm2 unstartup systemd
Now as the service user run the following (again use sudo su yourserviceuser
to change back)
Save
the new PM2
process list and environment.pm2 save
[PM2] Saving current process list...
[PM2] Successfully saved in /home/your-name/.pm2/dump.pm2
sudo reboot
command. You will need to login again with your service user and then run pm2 list
and systemctl status pm2-your-name
to verify everything is working.Below are some optional additions to secure your virtual machine and Strapi service.
Azure virtual machines come with a firewall at the host level (see previous instructions here). However it is recommended that you use the built in Ubuntu firewall as well known as UFW or Uncomplicated Firewall.
See the following DigitalOcean guide to see some examples of using UFW.
There are many different types of proxy services you could use, anything from load balancing, offloading SSL, to fault tolerance. You can view a few examples based around Strapi and basic SSL offloading.
There are many options for storing files outside of your virtual machine, Strapi have built a few and the community is constantly building new ones. See the following guide on searching for options as well as installing them.