cover image
< Home
Cloud

Deploy Strapi with Docker on Digital Ocean App Platform

The headless open source content management system (CMS) Strapi can be dockerized and deployed via Docker to the cloud. In this article I want to show how to deploy Strapi to the Digital Ocean App Platform as a Docker container.

Strapi

Strapi is a headless open source content management (CMS) created by an originally French startup. Strapi Inc. is now based in the U.S.A. with team members distributed globally.

Strapi as a headless CMS means, it uses an API (REST or GraphQL) to communicate with the frontend. Usually, the frontend retrieves the content via the API and publishes it on a website. The frontend can for example be written in Next.js or in any other framework that allows publishing of websites. Strapi allows for easy decription of the content types and integrates an easy editor for editing the actual content. The best is that the basic version of Strapi is open source and free to use.

Once you have defined your content model you usually want to deploy Strapi somewhere so it is accessible to the persons doing the actual content editing. This is where cloud platforms come in.

In my setup here I use the Digital Ocean cloud platform because it is easy to use.

Digital Ocean

Digital Ocean is a cloud platform that focuses on easy of use and developer experience. While by no means as comprehensive as AWS or other big cloud platforms, it nonetheless contains the basic services needed for operating in the cloud.

One possible way of deploying your application to Digital Ocean is using Docker and Docker images. Digital Ocean contains a Docker image registry and an "App Platform" to deploy the Docker images on actual servers. As a database for Strapi I use Digital Ocean's managed Postgres database.

Whiles there exists already a guide on Strapi's website for deploying Strapi on Digital Ocean, that guide does not use Docker. This is why I want to talk more about Docker in this article.

Building Docker Image

In my setup, I have a .env file with all the environmental variables needed for Strapi to operate. These include the database connection parameters. In my bash script I use the application dotenvx to pass the environmental variables of the .env file to the docker build command. Dotenvx is a Node application and can be installed by npm.

Here is my Bash script to build the image locally:

docker-build.sh

echo "Building with Docker..."
./increment-version.sh

./node_modules/.bin/dotenvx run -- sh -c 'docker build \
  --build-arg NODE_ENV=$NODE_ENV \
  --build-arg HOST=$HOST \
  --build-arg PORT=$PORT \
  --build-arg APP_KEYS=$APP_KEYS \
  --build-arg API_TOKEN_SALT=$API_TOKEN_SALT \
  --build-arg ADMIN_JWT_SECRET=$ADMIN_JWT_SECRET \
  --build-arg TRANSFER_TOKEN_SALT=$TRANSFER_TOKEN_SALT \
  --build-arg DATABASE_CLIENT=$DATABASE_CLIENT \
  --build-arg DATABASE_HOST=$DATABASE_HOST \
  --build-arg DATABASE_PORT=$DATABASE_PORT \
  --build-arg DATABASE_NAME=$DATABASE_NAME \
  --build-arg DATABASE_USERNAME=$DATABASE_USERNAME \
  --build-arg DATABASE_PASSWORD=$DATABASE_PASSWORD \
  --build-arg JWT_SECRET=$JWT_SECRET \
  --build-arg CLOUDINARY_NAME=$CLOUDINARY_NAME \
  --build-arg CLOUDINARY_KEY=$CLOUDINARY_KEY \
  --build-arg CLOUDINARY_SECRET=$CLOUDINARY_SECRET \
  -t strapi-app-name -f Dockerfile . '

This script runs a Docker build command to create the Docker image locally. It depends on the Dockerfile. The Dockerfile specifies the contents of the Docker image.

Here is my version:

Dockerfile

FROM node:20-alpine as build
# Installing libvips-dev for sharp Compatibility
RUN apk update && apk add --no-cache build-base gcc autoconf automake zlib-dev libpng-dev vips-dev > /dev/null 2>&1
ARG NODE_ENV=production
ARG HOST=0.0.0.0
ARG PORT=1337
ARG APP_KEYS
ARG API_TOKEN_SALT
ARG ADMIN_JWT_SECRET
ARG TRANSFER_TOKEN_SALT
ARG DATABASE_CLIENT
ARG DATABASE_HOST
ARG DATABASE_PORT
ARG DATABASE_NAME
ARG DATABASE_USERNAME
ARG DATABASE_PASSWORD
ARG JWT_SECRET
ARG CLOUDINARY_NAME
ARG CLOUDINARY_KEY
ARG CLOUDINARY_SECRET

ENV NODE_ENV=${NODE_ENV}
ENV HOST=${HOST}
ENV PORT=${PORT}
ENV APP_KEYS=${APP_KEYS}
ENV API_TOKEN_SALT=${API_TOKEN_SALT}
ENV ADMIN_JWT_SECRET=${ADMIN_JWT_SECRET}
ENV TRANSFER_TOKEN_SALT=${TRANSFER_TOKEN_SALT}
ENV DATABASE_CLIENT=${DATABASE_CLIENT}
ENV DATABASE_HOST=${DATABASE_HOST}
ENV DATABASE_PORT=${DATABASE_PORT}
ENV DATABASE_NAME=${DATABASE_NAME}
ENV DATABASE_USERNAME=${DATABASE_USERNAME}
ENV DATABASE_PASSWORD=${DATABASE_PASSWORD}
ENV JWT_SECRET=${JWT_SECRET}
ENV CLOUDINARY_NAME=${CLOUDINARY_NAME}
ENV CLOUDINARY_KEY=${CLOUDINARY_KEY}
ENV CLOUDINARY_SECRET=${CLOUDINARY_SECRET}

WORKDIR /opt/
COPY ./package.json ./yarn.lock ./
ENV PATH /opt/node_modules/.bin:$PATH
RUN yarn config set network-timeout 600000 -g && yarn install --production
WORKDIR /opt/app
COPY ./ .
RUN yarn build
FROM node:20-alpine
RUN apk add --no-cache vips-dev
ARG NODE_ENV=production
ARG HOST=0.0.0.0
ARG PORT=1337
ARG APP_KEYS
ARG API_TOKEN_SALT
ARG ADMIN_JWT_SECRET
ARG TRANSFER_TOKEN_SALT
ARG DATABASE_CLIENT
ARG DATABASE_HOST
ARG DATABASE_PORT
ARG DATABASE_NAME
ARG DATABASE_USERNAME
ARG DATABASE_PASSWORD
ARG JWT_SECRET
ARG CLOUDINARY_NAME
ARG CLOUDINARY_KEY
ARG CLOUDINARY_SECRET

ENV NODE_ENV=${NODE_ENV}
ENV HOST=${HOST}
ENV PORT=${PORT}
ENV APP_KEYS=${APP_KEYS}
ENV API_TOKEN_SALT=${API_TOKEN_SALT}
ENV ADMIN_JWT_SECRET=${ADMIN_JWT_SECRET}
ENV TRANSFER_TOKEN_SALT=${TRANSFER_TOKEN_SALT}
ENV DATABASE_CLIENT=${DATABASE_CLIENT}
ENV DATABASE_HOST=${DATABASE_HOST}
ENV DATABASE_PORT=${DATABASE_PORT}
ENV DATABASE_NAME=${DATABASE_NAME}
ENV DATABASE_USERNAME=${DATABASE_USERNAME}
ENV DATABASE_PASSWORD=${DATABASE_PASSWORD}
ENV JWT_SECRET=${JWT_SECRET}
ENV CLOUDINARY_NAME=${CLOUDINARY_NAME}
ENV CLOUDINARY_KEY=${CLOUDINARY_KEY}
ENV CLOUDINARY_SECRET=${CLOUDINARY_SECRET}
WORKDIR /opt/app
COPY --from=build /opt/node_modules ./node_modules
ENV PATH /opt/node_modules/.bin:$PATH
COPY --from=build /opt/app ./
EXPOSE 1337
CMD ["yarn", "start"]

As you can see, the environmental variables from the .env file are used in the Dockerfile.

The Bash script to do the image version management is as follows:

increment-version.sh

#!/bin/bash

# Read the current version number from the file
VERSION=$(cat version.txt)

# Increment the version number
IFS='.' read -ra PARTS <<< "$VERSION"
MAJOR=${PARTS[0]}
MINOR=${PARTS[1]}

# Increment the minor version number
NEW_MINOR=$((MINOR + 1))

# Combine the major and minor parts into a new version number
NEW_VERSION="$MAJOR.$NEW_MINOR"

# Write the new version number back to the file
echo $NEW_VERSION > version.txt

# Print the new version number
echo "New version number: $NEW_VERSION"

Now every time to start building the Docker image, a new version number is created.

Uploading Docker Image

Once you have created the Docker image locally, you can upload it to the Digital Ocean Docker Registry. From there you can deploy it to a server.

Here is my Bash script for uploading the Docker image:

push-image.sh

echo "Pushing image to Digital Ocean..."

VERSION=$(cat version.txt)

docker tag strapi-app-name registry.digitalocean.com/your-containers/strapi-app-name:$VERSION
docker push registry.digitalocean.com/your-containers/strapi-app-name:$VERSION

You have to adapt the names of your-containers and strapi-app-name to your specific values. The Bash script supports incrementally versioning the Docker images.

Creating Digital Ocean App

Once the Docker image is in the registry you can deploy it in the Digital Ocean web interface.

Go to "App Platform". Click "Create App". Choose "Digital Ocean Container Registry". Then choose your repository and tag. Next, define your server hardware and as public HTTP port enter "1337". No "Run Command" needs to be entered.

Once you click on "Create", the app should be deployed on your server and subsequently it should be accessible in the defined URL.

Conclusion

Strapi as a popular open source CMS needs to be deployed somewhere so that editors can access it and edit content. As you could see, one possibility among a variety of possibilities is deploying the Strapi CMS on Digital Ocean using Docker.

If you found this interesting or would like to tell me your experience with deploying Docker, please contact me, by pressing the contact button below.

References

Strapi: https://strapi.io

Digital Ocean: https://www.digitalocean.com

Docker: https://www.docker.com

Photo by Lucas K on Unsplash

Published

30 Aug 2024

Thomas Derflinger

Written by Thomas Derflinger

I am a visionary entrepreneur and software developer. In this blog I mainly write about web programming and related topics like IoT.