In this blog, we will set up our own Strapi π setup using Docker, docker-compose π³.
We will also use docker-compose
to spin up a Postgres πΎ database and a local strapi instance.
π¦ Requirements
- Docker
- NPM / Yarn (Yarn is recommended)
- Node 14 is recommended but 16 will work if we are using yarn
- Your favourite code editor. (I will be using VSCode) π©βπ»
βοΈ Strapi Setup
Let's spin up a brand new V4 project.
npx create-strapi-app@latest myproject --quickstart
Note: Replace myproject with the project name
Once this is completed we can press CTRL + C
in the terminal to stop the current file, we don't need to fill in the file as this is using SQLite, and we will be using PostgreSQL
Let's open the folder now in vscode (In the terminal, you can try to use this nifty quick way of opening it cd myproject && code .
which will change to the project directory of strapi and open vscode.)
Note: Replace myproject with the project name
So let's get dockering shall we? π€ π³
π©βπ» Docker Setup
π³ The Dockerfile
Create a file called Dockerfile
(Note capital D)
If you are using YARN, please use the following π
FROM node:16
# Installing libvips-dev for sharp Compatability
RUN apt-get update && apt-get install libvips-dev -y
ARG NODE_ENV=development
ENV NODE_ENV=${NODE_ENV}
WORKDIR /opt/
COPY ./package.json ./yarn.lock ./
ENV PATH /opt/node_modules/.bin:$PATH
RUN yarn config set network-timeout 600000 -g && yarn install
WORKDIR /opt/app
COPY ./ .
RUN yarn build
EXPOSE 1337
CMD ["yarn", "develop"]
If NPM was your β οΈ, then please use the following π
FROM node:16
# Installing libvips-dev for sharp Compatability
RUN apt-get update && apt-get install libvips-dev -y
ARG NODE_ENV=development
ENV NODE_ENV=${NODE_ENV}
WORKDIR /opt/
COPY ./package.json ./package-lock.json ./
ENV PATH /opt/node_modules/.bin:$PATH
RUN npm install
WORKDIR /opt/app
COPY ./ .
RUN npm run build
EXPOSE 1337
CMD ["npm", "run", "develop"]
Let's step through this gibberish of some code for those that have not used Docker π³before.
- First, we get a node image 16 (We can use :14) if we wanted to use version 14 of node instead.
- We are setting an argument as default to develop as we want to not have to provide this every time we run our setup. The
ENV
is a way to override it if we want to likeproduction
WORKDIR
we are creating a folder inside our container π where ournode_modules
will live- We copy package.json and yarn.lock (or package-lock.json if you use npm) into our work directory. We do this FIRST as π³ docker caches each layer and this will then speed up our build process. Unless the file changes. π
- We then tell Docker where to look for our
node_modules
- In case we have some network issues or a bit of slow internet we are setting a large timeout to give it some extra time.
- We then run
yarn install
to install all dependencies. - We then change directories into
/opt/app
- We then copy the project that we created first into this folder.
- Then we run
yarn build
to build our strapi project. - At the end, we expose the port
1337
and tell Docker to runyarn develop
π ββοΈ Let's ignore this
Create a file called .dockerignore
(Note .
in front)
.dockerignore
.tmp/
.cache/
.git/
build/
node_modules/
data/
This works like a .gitignore
and will tell Docker π³ not to transfer βοΈ these folders as we don't need them.
π·ββοΈ Let's build the image π
We can now build this image in a simple way.
docker build -t mystrapi:latest .
Note that mystrapi is the name of the image, and using :latest can be anything like an example
docker build -t bestbackendever:1.7.7 .
This will create an image called bestbackendever and be version 1.7.7
Now go get some βοΈ and sit back while Docker does its magic π³ πͺ = β€οΈ
Once this is completed as it can take a bit of time (Normally a few minutes βοΈ) then we can run our project.
docker run -d -p 1337:1337 mystrapi
This tells Docker to run the image mystrapi
or whatever you called your project π€ on port 1337 -d
means detached and is a fancy word of saying "Runs in the background"
Cool tip if you want to use strapi on a different port while developing you can change first part of the run port to something else like so
docker run -d -p 8080:1337 mystrapi
And it will now run on port 8080
π π
Now, this won't help us much as currently, we are using an SQLite
database, and it's always inside the container. When we stop the container, we lose all changes.
So what we are doing to do is to use docker-compose
to have a Postgres database and be able to run multiple instances of Docker if we want.
πββοΈ Stepping up our game with docker-compose
So let's really use the power for development with some docker magic. π³ πͺ Think of docker-compose of a way of making different steps or services that we want to run.
Create a file in the root of the project call it docker-compose.yml
version: "3"
services:
strapi:
container_name: strapi
build: .
image: mystrapi:latest
restart: unless-stopped
env_file: .env
environment:
DATABASE_CLIENT: ${DATABASE_CLIENT}
DATABASE_HOST: strapiDB
DATABASE_NAME: ${DATABASE_NAME}
DATABASE_USERNAME: ${DATABASE_USERNAME}
DATABASE_PORT: ${DATABASE_PORT}
JWT_SECRET: ${JWT_SECRET}
ADMIN_JWT_SECRET: ${ADMIN_JWT_SECRET}
DATABASE_PASSWORD: ${DATABASE_PASSWORD}
NODE_ENV: ${NODE_ENV}
volumes:
- ./config:/opt/app/config
- ./src:/opt/app/src
- ./package.json:/opt/package.json
- ./yarn.lock:/opt/yarn.lock # Replace with package-lock.json if using npm
- ./.env:/opt/app/.env
ports:
- "1337:1337"
networks:
- strapi
depends_on:
- strapiDB
strapiDB:
image: postgres:12.0-alpine
container_name: strapiDB
platform: linux/amd64 #for platform error on Apple M1 chips
restart: unless-stopped
env_file: .env
environment:
POSTGRES_USER: ${DATABASE_USERNAME}
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
POSTGRES_DB: ${DATABASE_NAME}
volumes:
- strapi-data:/var/lib/postgresql/data/ #using a volume
#- ./data:/var/lib/postgresql/data/ # if you want to use a bind folder
ports:
- "5432:5432"
networks:
- strapi
volumes:
strapi-data:
networks:
strapi:
name: Strapi
driver: bridge
This is a YAML file so spacing matters β’οΈ.
I use spaces, not tabs on this to keep consistent.
So to explain what all this is.
version
- Using version 3 of docker-compose for info read here
services
- We are defining two services strapi
and strapiDB
strapi
- this is the name of the service we definedcontaier_name
- The term of the docker container you can call this whatever you want.build
- Telling strapi to build the image using our project folder. (Since this is in the root.
) it will use our strapi project.image
- The name of the image we want to buildrestart
- Unless we STOP the container or take it down it will keep restarting.env_file
providing a.env
with environmental variables we want to keep secret π€«environment
- Here we are defining all the variables we want to use.${THISISOURNAME}
is what we have in the.env
and will be the placeholdervolumes
mounting files into the container. Now this could be./:/opt/app
, but we might want to develop locally and just run our development server locally we are binding folders and some files to not bindnode_modules
There is some info about that here.ports
- What ports we want to expose Note: You can change the left side to use a different port like 8080:1337, remember right side needs to be 1337 as that is what is inside the container where strapi is runningnetworks
- We are setting up a docker network so our containers can talk together, this can also replacelink
-depends_on
- This is a bit deprecated in newer version but i like to use it. It tells Docker that to runstrapi
container, we need thepostgresDB
container to run first. Saving us some errors when strapi starts and there is no database.
Then we get to Postgres same thing here,
We are giving it a name, but we are using the official Postgres12-alpine
image no need to build it ourselves.
We are also creating a volume called strapi-data
, which holds our database.
π¦Bind-mounting vs Docker Volume
Bind-mounting is a way to tell Docker to use a specific FOLDER that we got control over. Though I found out it's easier to use a docker volume instead. But if you want to do it, please change the line in docker-compose
as directed by comments under volumes.
π©βπ¬ Creating Bind mount folders
So if you wanted to go down this route as the old guide was showing π
In your root folder create a folder call it data
Now let's update .gitignore
and add it like so
.gitignore
...
data/
As we don't want to commit these files, it's the whole database π±.
Right so now we are almost set!
π§ Changing SQLite to Postgres / MySQL
β’οΈ First off if you want to use MySQL / MariaDB instead of Postgres please replace the strapiDB
in docker-compose
with the following π
strapiDB:
image: mysql:latest
command: --default-authentication-plugin=mysql_native_password
platform: linux/amd64 #for platform error on Apple M1 chips
restart: always
environment:
MYSQL_ROOT_PASSWORD: ${DATABASE_PASSWORD}
MYSQL_DATABASE: ${DATABASE_NAME}
MYSQL_USER: ${DATABASE_USERNAME}
MYSQL_PASSWORD: ${DATABASE_PASSWORD}
ports:
- '3306:3306'
volumes:
- strapi-data:/var/lib/mysql #using a volume
#- ./data:/var/lib/mysql # if you want to use a bind folder
networks:
- strapi
We can say that it's per environment or set it for all environments. For this tutorial we are going to use Postgres so we are going to set it to all. For more info about environments configs please read here
But again, for this tutorial, we will use Postgres for local development AND production, etc. The benefit here is that we can easily export data later if we need it in production etc.
So let's get cracking.
Open the following file. config/database.js
and replace it with this.
module.exports = ({ env }) => ({
connection: {
client: env("DATABASE_CLIENT", "postgres"),
connection: {
host: env("DATABASE_HOST", "127.0.0.1"),
port: env.int("DATABASE_PORT", 5432),
database: env("DATABASE_NAME", "strapi"),
user: env("DATABASE_USERNAME", "strapi"),
password: env("DATABASE_PASSWORD", "strapi"),
},
debug: false,
},
});
});
π«°PRO TIP if you are using MySQL/MariaDB replace client
with mysql
. This also applies in your .env
as well, the port will change to 3306
instead of 5432
β’οΈ Important step below π
Make sure that you install either pg
or mysql
using yarn
or npm
# Select one
yarn add pg
yarn add mysql
npm install pg
npm install mysql
else it will fail and you have to rebuild images later.
This now tells strapi that we want to set the database host etc.
So all that is missing is to set up our .env
so let's set that up.
Open up .env
if there is no file rename .env.example
to .env
...
DATABASE_HOST=localhost
DATABASE_PORT=5432
#DATABASE_PORT=3306
DATABASE_NAME=strapi
DATABASE_USERNAME=strapi
DATABASE_PASSWORD=strapi
NODE_ENV=development
DATABASE_CLIENT=postgres
#DATABASE_CLIENT=mysql
In total you should now have a .env
that looks like this
HOST=0.0.0.0
PORT=1337
APP_KEYS=XXX
API_TOKEN_SALT=XXX
ADMIN_JWT_SECRET=XXX
JWT_SECRET=XXX
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_NAME=strapi
DATABASE_USERNAME=strapi
DATABASE_PASSWORD=strapi
NODE_ENV=development
DATABASE_CLIENT=postgres
π Running our project
Local - docker-compose up -d strapiDB && yarn develop
- This will now spin up just a Postgres database, and we can run and change files just like working on strapi anywhere.
π«°PRO TIP- replace yarn develop
with npm run
develop if using npm
Full - docker-compose up -d' - This will run
strapi` inside a docker container and the database in its own container.
π Self-Sponsored Tool
If you did not follow the previous guide but want to get started. I have created a tool that will dockerize the whole app and get you upt to speed to this guide.
In your strapi application just run
npx @strapi-community/dockerize
If you on purpose select PostgresSQL
and yes
to docker-compose
in the tool most of the steps here should be a breeze.
If you liked the tool feel free to βοΈ it on Github
π Updates
π Update: 10/07/22 Self-Sponsored Tool section where you can use my tool to automate the blog post.
π Update: 07/02/22
Also updated the docker container to add support for libvips-dev
to support sharp on build.
π¦ Update: 10/06/22
Thanks to kalle for the test of this.
π Update: 10/06/22
- Updated docker-compose to now use
volumes
instead of bind mount - Added
MYSQL/MariaDB
thanks to Dennis M from the strapi team - Added npm setup
- Configured yarn set up to be updated (Moved to a single step)
- Updated variables to match the latest version, 4.1.12
- Added
pg
andmysql
to be installed
Hope this helps with any questions please let me know in the comment section below. π or do pour me a πΊ or feed that π¦ at the right hand side.