🐳 Docker with Strapi V4 🌟

🐳 Docker with Strapi V4 🌟

Dockerize Strapi for development

Simen Daehlin's photo
Simen Daehlin
Β·Jan 10, 2022Β·

8 min read

Subscribe to my newsletter and never miss my upcoming articles

Table of contents

  • Requirements
  • Development Docker

In this blog, we are going to set up our own Strapi 🌟 setup using docker, docker-compose 🐳. We are also going to use docker-compose to spin up a Postgres πŸ’Ύ database and our own local strapi instance.

Requirements

  • Docker
  • NPM / Yarn (Yarn is recommended) (Node 14 is recommended but 16 will work if we are using yarn for local development)
  • Your favourite code editor. (I will be using VSCode) πŸ‘©β€πŸ’»

Development Docker

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? πŸ€” 🐳

Create a file called Dockerfile (Note capital D)

FROM node:16
# Installing libvips-dev for sharp compatability
RUN apt-get update && apt-get install libvips-dev
ARG NODE_ENV=development
ENV NODE_ENV=${NODE_ENV}
WORKDIR /opt/
COPY ./package.json ./
COPY ./yarn.lock ./
ENV PATH /opt/node_modules/.bin:$PATH
RUN yarn config set network-timeout 600000 -g
RUN yarn install
WORKDIR /opt/app
COPY ./ .
RUN yarn build
EXPOSE 1337
CMD ["yarn", "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 like production
  • WORKDIR we are creating a folder inside our container πŸš€ where our node_modules will live
  • We copy package.json and yarn.lock 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 run yarn develop

Create a file called .dockerignore (Note . infront)

.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.

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.

screenshot.png

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 a 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.

Using 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: postgresDB
      DATABASE_NAME: ${DATABASE_NAME}
      DATABASE_USERNAME: ${DATABASE_USERNAME}
      DATABASE_PORT: ${DATABASE_PORT}
      JWT_SECRET: ${JWT_SECRET}
      DATABASE_PASSWORD: ${DATABASE_PASSWORD}
      NODE_ENV: ${NODE_ENV}
    links:
      - postgresDB:postgresDB
    volumes:
      - ./config:/opt/app/config
      - ./src:/opt/app/src
      - ./package.json:/opt/package.json
      - ./yarn.lock:/opt/yarn.lock
      - ./.env:/opt/app/.env
    ports:
      - "1337:1337"
    networks:
      - strapi
    depends_on:
      - postgresDB

  postgresDB:
    image: postgres:12.0-alpine
    container_name: postgresDB
    restart: unless-stopped
    env_file: .env
    environment:
      POSTGRES_USER: ${DATABASE_USERNAME}
      POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
      POSTGRES_DB: ${DATABASE_NAME}
    volumes:
      - ./data:/var/lib/postgresql/data/
    ports:
      - "5432:5432"
    networks:
      - strapi

networks:
  strapi:
    name: Strapi
    driver: bridge

This is a YAML file so spacing matters. I normally use spaces not tabs on this just 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 postgresDB

strapi - this is the name of the service we defined contaier_name - The name 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 build restart - 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 placeholder links - We want our strapi container to talk to postgresDB so this tells it to be a link volumes mounting files into the container. Now this could just be ./:/opt/app but due to that we might want to develop locally and just run our development server locally we are binding folders and some files to not bind node_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 running networks - We are setting up a docker network so our containers can talk together, this can also replace link depends_on - This is a bit deprecated in newer version but i like to use it. It tells docker that to run strapi container we need the postgresDB 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 now also binding the data folder into the container so let's create that

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.

Right so now we are almost set!

Changing SQLite to Postgres

Now there are two ways of doing this.

We can say that it's per environment or we can 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 are going to use Postgres for local development AND for 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,
},
});

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_NAME=strapi
DATABASE_USERNAME=strapi
DATABASE_PASSWORD=strapi
NODE_ENV=development
DATABASE_CLIENT=postgres

And that's it we now have multiple ways of running our project.

Local - docker-compose up -d postgresDB && yarn develop - This will now spin up just a Postgres database and we can run and change files just like working on strapi anywhere.

Full - docker-compose up -d - This will run strapi inside a docker container and the database in its own container.

Hope this helps with any questions please let me know in the comment section below. πŸ‘‡

Update: 07/02/22

  • We need to install some dependencies for Postgres. This will add bookshelf, Postgres and knex which is used as a query builder.
    yarn add pg knex strapi-connector-bookshelf
    
    Also updated the docker container to add support for libvips-dev to support sharp on build.

Thanks to kalle for the test of this.

Did you find this article valuable?

Support Simen Daehlin by becoming a sponsor. Any amount is appreciated!

Learn more about Hashnode Sponsors
Β 
Share this