Reducing Strapi Docker Image Size

Hey guys! I am fairly new to Docker and just managed to build my first few images with this Dockerfile:

FROM strapi/base

WORKDIR /src/app

COPY ./package.json ./
COPY ./yarn.lock ./

RUN yarn install

COPY . .

ENV NODE_ENV production

RUN yarn build

EXPOSE 1337

CMD ["yarn", "start"]

It works, however the image size is about 1.83GB big. I have read a bit about multistage docker builds and how they can drastically reduce the size. Unfortunately I am not sure how to set it up though.

I also tried to build the image with FROM: node:alpine but the file size was still 1GB and Strapi failed to run with yarn start due to a problem with the node sharp module.

Any advice for me to decrease the image size? Highly appreciate your help for a Docker newbie.

Try to add node_modules to docker ignore file.

Try to install build-essential after FROM: node
RUN apt-get install -y build-essential

Also, look at the docker file for node alpine to understand what other requirements you need to install.

Thanks for the super fast answer!

I had the node_modules folder in my .gitignore already.

Regarding the other two parts. Thanks for clarifying the sharp problem. The missing build-essential is probably what caused the problem.

Would it be possible for you to post an example Dockerfile :whale: of what you exactly mean? I looked at the base/alpine files but can’t wrap my head around yet how to setup the full Dockerfile now…

Not gitingore, Dockerignore.
You should add there all the unnecessary files. Like .tmp, .git, node_modules and others.

Also I would recommend to take a look at this article to see how you could reduce docker image size even more:

Sorry, I meant .dockerignore not .gitignore. I will take a look at your article, thanks!

1 Like

Don’t forget to add the .git directory to dockerignore. Since it is also copied into the image, with all the commits history, branches and etc. If you have a big repo with 10-20 branches of your project that can increase the image’s size a lot.

I don’t have a complete Dockerfile for alpine, since I’m using Debian, but they must be pretty similar. You can take a look at one of my docker files for google cloud by using debian, I’ve also left a few comments inside it:

#use debian image
FROM debian:10

#install all the dependencies that are need on debian to run strapi project with nginx and pm2.
RUN apt-get -y update
RUN apt-get install -y git ca-certificates nginx-full bash nano ssl-cert curl gzip zip python make gcc g++ wget gnupg2 dialog apt-utils
RUN curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
RUN apt-get update && apt-get install -y yarn
RUN apt-get install -y build-essential
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash - \
    && apt-get install -y nodejs

#copy only the files that I need to run the project 
RUN mkdir www/
COPY api/ /www/api
COPY admin/ /www/admin
COPY config/ /www/config/
COPY extensions/ /www/extensions
COPY public/ /www/public
COPY docker/ /www/docker/
COPY plugins/ /www/plugins/
COPY components/ /www/components/
COPY package.json /www/
COPY ecosystem.config.js /www/
COPY /docker/nginx/nginx.conf /etc/nginx/

WORKDIR /www

#making the sh files executable, you can avoid this part.
RUN chmod +x ./docker/entrypoint.sh
RUN chmod +x ./docker/cloud_sql_proxy

RUN echo "unsafe-perm = true" >> ~/.npmrc

#install only the production packages, also reduces the size/time of install
RUN yarn install --only=production && yarn global add pm2

#building the admin panel for production 
RUN NODE_ENV=production yarn build

VOLUME /etc/www

EXPOSE 1337
EXPOSE 80
STOPSIGNAL SIGTERM

RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf

RUN make-ssl-cert generate-default-snakeoil --force-overwrite

#In entrypoint we start nginx/gcloud SQL proxy. And as the last step it runs the pm2 process with ecosystem config file configured for strapi.
#that part can also be avoided, you can use yarn start if you don't want to use pm2.
ENTRYPOINT /www/docker/entrypoint.sh
1 Like

Hi, I want share my Dockerfile, run on stock Strapi v3.6.3.

  • Use docker Buildkit.
  • Build time about 2’30s on Macbook Pro 2015.
  • Image size about ~500MB.

Hope this can help you. ^^

FROM node:14-alpine as BUILD_IMAGE

WORKDIR /strapi

# Resolve node_modules for caching
COPY ./package.json ./
COPY ./yarn.lock ./
RUN yarn install --production=true --frozen-lockfile

# Copy all for build and release cache if package.json update
COPY . .
ENV NODE_ENV=production

RUN yarn build

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

# Create new namespace for final Docker Image
FROM node:14-alpine

# Only copy your source code without system file
COPY --from=BUILD_IMAGE /strapi /strapi

WORKDIR /strapi

EXPOSE 1337

ENV NODE_ENV=production
ENV STRAPI_LOG_LEVEL=debug

CMD ["yarn", "start"]
4 Likes

Hi all,
I typically deploy Strapi backend separately from the front-end. The front-end is Webpack’d and deployed on static hosting with CDN.

Now, when deploying a Docker image of the backend, even at 500MB, it is huge! All of the front-end dependencies (Webpack, React, etc) are essentially installed along with it. Either if they are packaged in the image, or installed when the image is built, the entire huge dependency tree of the front-end and back-end are pulled in at some point.

Because of how Strapi is structured, I haven’t figured out a way to omit all the front-end dependencies when deploying the back-end separately…

Has anyone had thoughts about this?

1 Like

To me it is confusing that in production (after running the build command) you still need the src folder present. Why is this? is it possible to somehow only serve the static files in the build folder? If not what is the purpose of this build folder?

Hi all,
I’ve published a utility called lockfile-shaker and a corresponding configuration for Strapi lockfile-shaker-strapi.

Basically what it does is edit your package-lock.json to safely make all of the dependencies that are only needed for the Strapi Admin front-end into dev-only dependencies. Then when you run npm ci --only production in your Docker build, it will only install the dependencies that are required for the backend server to run (not React, Webpack and all of those only require for the front-end) .

I use this in production and it reduces the number of dependencies in my server Docker image by 1350 packages, saving image build time, and reducing the image size by several hundred MB.

When you create content types you will see new folders/files for each type added under src/api directory. It also contains two other folders: admin and extensions. None of this is actual source code, it’s part of how strapi operates. So the name src might just be misleading?

A bit late to the party but I made a tool for this

This now has a production option and the Dockerfile.prod will be around 500-600MB depending if you are using npm or yarn (yes it’s a difference yarn is smaller)

2 Likes