How to specify a custom Dockerfile¶
To specify a custom Dockerfile that will be used to build your application you should go to application settings, and on the first tab "Build Settings"
Select Custom Dockerfile from the list
The text field "Custom Dockerfile" will appear under the dropdown.
In this text field put the Dockerfile.
Directory structure:¶
During the build Dockerfiles are executed in the context of a directory, one level higher than your code.
On the server structure of an application directory looks like this:
# ls /home/app/someapp
Dockerfile
code
docker-compose.yml
env/dot.env
env/envs_export.sh
The Dockerfile
will contain whatever you will pick - custom or predefined.
code
is the working copy of your app's git repo.
docker-compose.yml
is assembled from the processes enabled in the dashboard in Processes
tab.
env/dot.env
are env variables in the dotenv format, for example:
DJANGO_COLLECTSTATIC=1
DJANGO_DEBUG=False
DJANGO_ALLOWED_HOSTS=something.applikuapp.com
This file is used in docker-compose.yml
env/envs_export.sh
also contains environment variables, but in a different format:
export DJANGO_COLLECTSTATIC="1"
export DJANGO_DEBUG="False"
export DJANGO_ALLOWED_HOSTS="something.applikuapp.com"
This file may be used in dockerfile if you need environment variables during the build.
In order to write your own Dockerfile make sure you COPY
your project files from /code
, not from .
Predefined Dockerfiles¶
Here are the list of some predefined Dockerfiles which you can grab and customize to your project's needs.
Python-3.12 Dockerfile¶
FROM python:3.12.3-bullseye
SHELL ["/bin/bash", "-c"]
ENV PIP_NO_CACHE_DIR off
ENV PIP_DISABLE_PIP_VERSION_CHECK on
ENV PYTHONUNBUFFERED 1
ENV PYTHONDONTWRITEBYTECODE 0
RUN apt-get update \
&& apt-get install -y --force-yes \
nano python3-pip gettext chrpath libssl-dev libxft-dev \
libfreetype6 libfreetype6-dev libfontconfig1 libfontconfig1-dev\
&& rm -rf /var/lib/apt/lists/*
RUN pip install --upgrade pip && pip install --upgrade setuptools && pip install gunicorn
WORKDIR /code/
COPY ./code/requirements.txt /code/
RUN pip install -r requirements.txt
COPY ./code/ /code/
COPY ./env/ /env/
RUN source /env/envs_export.sh && if [ -n "$BUILD_COMMAND" ]; then eval $BUILD_COMMAND; fi
RUN source /env/envs_export.sh && export && if [ -f "manage.py" ]; then if [ "$DISABLE_COLLECTSTATIC" == "1" ]; then echo "collect static disabled"; else echo "Found manage.py, running collectstatic" && python manage.py collectstatic --noinput; fi; else echo "No manage.py found. Skipping collectstatic."; fi;
RUN useradd -ms /bin/bash code
USER code
Python-3.12 + Node 20 NPM Dockerfile¶
Let's look how the predefined Dockerfile for python 3.12 + Node 20 NPM looks like:
FROM python:3.12.3-bullseye
SHELL ["/bin/bash", "-c"]
ENV PIP_NO_CACHE_DIR off
ENV PIP_DISABLE_PIP_VERSION_CHECK on
ENV PYTHONUNBUFFERED 1
ENV PYTHONDONTWRITEBYTECODE 0
RUN apt-get update \
&& apt-get install -y --force-yes \
nano python3-pip gettext chrpath libssl-dev libxft-dev \
libfreetype6 libfreetype6-dev libfontconfig1 libfontconfig1-dev\
&& rm -rf /var/lib/apt/lists/*
ENV NODE_VERSION=20.10.0
ENV NVM_DIR=/root/.nvm
RUN apt install -y curl
RUN curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.39.7/install.sh | bash
RUN . "$NVM_DIR/nvm.sh" && nvm install ${NODE_VERSION}
RUN . "$NVM_DIR/nvm.sh" && nvm use v${NODE_VERSION}
RUN . "$NVM_DIR/nvm.sh" && nvm alias default v${NODE_VERSION}
ENV PATH="/root/.nvm/versions/node/v${NODE_VERSION}/bin/:${PATH}"
RUN node --version
RUN npm --version
RUN npm install --global yarn@1.22.21
RUN pip install --upgrade pip && pip install --upgrade setuptools && pip install gunicorn
WORKDIR /code/
COPY ./code/requirements.txt /code/
RUN pip install -r requirements.txt
COPY ./code/ /code/
COPY ./env/ /env/
RUN source /env/envs_export.sh && if [ -n "$BUILD_COMMAND" ]; then eval $BUILD_COMMAND; fi
RUN source /env/envs_export.sh && export && if [ -f "manage.py" ]; then if [ "$DISABLE_COLLECTSTATIC" == "1" ]; then echo "collect static disabled"; else echo "Found manage.py, running collectstatic" && python manage.py collectstatic --noinput; fi; else echo "No manage.py found. Skipping collectstatic."; fi;
RUN useradd -ms /bin/bash code
USER code
Python 3.11 with Chrome Browser Dockerfile¶
FROM python:3.11.10-bullseye
SHELL ["/bin/bash", "-c"]
ENV PIP_NO_CACHE_DIR off
ENV PIP_DISABLE_PIP_VERSION_CHECK on
ENV PYTHONUNBUFFERED 1
ENV PYTHONDONTWRITEBYTECODE 0
ARG CHROME_VERSION=125.0.6422.141-1
ARG CHROME_DRIVER_VERSION=125.0.6422.141
RUN apt-get update && apt-get install -y \
wget \
unzip
RUN mkdir -p /usr/local/tmp \
&& cd /usr/local/tmp \
&& wget http://dl.google.com/linux/deb/pool/main/g/google-chrome-stable/google-chrome-stable_"$CHROME_VERSION"_amd64.deb \
&& apt-get install -y ./google-chrome-stable_"$CHROME_VERSION"_amd64.deb \
&& wget https://storage.googleapis.com/chrome-for-testing-public/"$CHROME_DRIVER_VERSION"/linux64/chromedriver-linux64.zip \
&& unzip chromedriver-linux64.zip \
&& mv chromedriver-linux64/chromedriver /usr/bin/chromedriver \
&& chmod +x /usr/bin/chromedriver
RUN apt-get update \
&& apt-get install -y --force-yes \
nano python3-pip gettext chrpath libssl-dev libxft-dev \
libfreetype6 libfreetype6-dev libfontconfig1 libfontconfig1-dev\
&& rm -rf /var/lib/apt/lists/*
RUN pip install --upgrade pip && pip install --upgrade setuptools && pip install gunicorn
WORKDIR /code/
COPY ./code/requirements.txt /code/
RUN pip install -r requirements.txt
COPY ./code/ /code/
COPY ./env/ /env/
RUN source /env/envs_export.sh && if [ -n "$BUILD_COMMAND" ]; then eval $BUILD_COMMAND; fi
RUN source /env/envs_export.sh && export && if [ -f "manage.py" ]; then if [ "$DISABLE_COLLECTSTATIC" == "1" ]; then echo "collect static disabled"; else echo "Found manage.py, running collectstatic" && python manage.py collectstatic --noinput; fi; else echo "No manage.py found. Skipping collectstatic."; fi;
RUN useradd -ms /bin/bash code
USER code
Node 20 NPM Dockerfile¶
FROM node:20-alpine
RUN apk add --no-cache libc6-compat build-base python3
WORKDIR /code
COPY code/package*.json ./
RUN npm install
COPY code/. .
COPY ./code/ /code/
COPY ./env/ /env/
RUN source /env/envs_export.sh && if [ -n "$BUILD_COMMAND" ]; then eval $BUILD_COMMAND; fi
Ruby Rails Dockerfile¶
# syntax = docker/dockerfile:1
# Make sure RUBY_VERSION matches the Ruby version in .ruby-version and Gemfile
ARG RUBY_VERSION=3.3.0
FROM registry.docker.com/library/ruby:$RUBY_VERSION-slim as base
SHELL ["/bin/bash", "-c"]
# Rails app lives here
WORKDIR /rails
# Set production environment
ENV RAILS_ENV="production" \
BUNDLE_DEPLOYMENT="1" \
BUNDLE_PATH="/usr/local/bundle" \
BUNDLE_WITHOUT="development"
# Throw-away build stage to reduce size of final image
FROM base as build
# Install packages needed to build gems
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y build-essential git libvips pkg-config libpq-dev curl
# Install application gems
COPY ./code/Gemfile ./code/Gemfile.lock ./
RUN bundle install && \
rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \
bundle exec bootsnap precompile --gemfile
RUN gem install pg
# Copy application code
COPY ./code .
COPY ./env/ /env/
ENV SECRET_KEY_BASE_DUMMY=1
# Precompile bootsnap code for faster boot times
RUN source /env/envs_export.sh && bundle exec bootsnap precompile app/ lib/
# Precompiling assets for production without requiring secret RAILS_MASTER_KEY
# RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile
RUN source /env/envs_export.sh && ./bin/rails assets:precompile
# Final stage for app image
FROM base
# Install packages needed for deployment
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y curl libpq-dev libvips && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives
# Copy built artifacts: gems, application
COPY --from=build /usr/local/bundle /usr/local/bundle
COPY --from=build /rails /rails
# Run and own only the runtime files as a non-root user for security
RUN useradd rails --create-home --shell /bin/bash && \
chown -R rails:rails db log storage tmp
USER rails:rails
# Entrypoint prepares the database.
ENTRYPOINT ["/rails/bin/docker-entrypoint"]
# Start the server by default, this can be overwritten at runtime
EXPOSE 3000
CMD ["./bin/rails", "server"]