Dockerfile for many programming languages
Contents
Dockerfile for React
Normal:
FROM node:20-alpine as build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:stable-alpine
COPY --from=build /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
With pnpm:
FROM node:20-alpine as build
RUN npm install -g pnpm
WORKDIR /app
COPY package*.json ./
RUN CI=true pnpm install
COPY . .
RUN pnpm build
FROM nginx:stable-alpine
COPY --from=build /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Nginx Config:
# auto detects a good number of processes to run
worker_processes auto;
#Provides the configuration file context in which the directives that affect connection processing are specified.
events {
# Sets the maximum number of simultaneous connections that can be opened by a worker process.
worker_connections 8000;
# Tells the worker to accept multiple connections at a time
multi_accept on;
}
http {
# what times to include
include /etc/nginx/mime.types;
# what is the default one
default_type application/octet-stream;
# Sets the path, format, and configuration for a buffered log write
log_format compression '$remote_addr - $remote_user [$time_local] '
'"$request" $status $upstream_addr '
'"$http_referer" "$http_user_agent"';
server {
# listen on port 80
listen 80;
# save logs here
access_log /var/log/nginx/access.log compression;
# where the root here
root /usr/share/nginx/html;
# what file to server as index
index index.html index.htm;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to redirecting to index.html
try_files $uri $uri/ /index.html;
}
# Media: images, icons, video, audio, HTC
location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
expires 1M;
access_log off;
add_header Cache-Control "public";
}
# Javascript and CSS files
location ~* \.(?:css|js)$ {
try_files $uri =404;
expires 1y;
access_log off;
add_header Cache-Control "public";
}
# Any route containing a file extension (e.g. /devicesfile.js)
location ~ ^.+\..+$ {
try_files $uri =404;
}
}
}
Dockerfile for NodeJS
ExpressJS:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
CMD ["node", "index.js"]
Install node-gyp on Node Alpine version:
FROM node:20-alpine
RUN apk add --no-cache \
make \
gcc \
g++ \
python3 \
pkgconfig \
pixman-dev \
cairo-dev \
pango-dev \
libjpeg-turbo-dev \
giflib-dev
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["node", "index.js"]
If you try to use the sharp package with NodeJS but encounter errors:
Could not load the "sharp" module using the linuxmusl-x64 runtime
sharp: Installation error: Invalid Version: 1.2.4_git20230717
Fix by changing FROM node:20-alpine
to FROM node:20-buster-slim
:
FROM node:20-buster-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
CMD ["node", "index.js"]
NestJS Framework:
FROM node:20-alpine as build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY --chown=node:node . .
RUN npm run build
# Running `npm ci` removes the existing node_modules directory and passing in --only=production ensures that only the production dependencies are installed. This ensures that the node_modules directory is as optimized as possible
RUN npm ci --only=production && npm cache clean --force
USER node
FROM node:20-alpine
WORKDIR /app
COPY --from=build --chown=node:node /app/package*.json ./
COPY --from=build --chown=node:node /app/node_modules ./node_modules
COPY --from=build --chown=node:node /app/dist ./dist
CMD ["node", "dist/main.js"]
Dockerfile for Python
Normal:
FROM python:3.9-slim-bullseye
WORKDIR /app
COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt
COPY . .
CMD ["python3", "app.py"]
With Flask or Django, you need to run on host 0.0.0.0
.
Flask:
FROM python:3.9-slim-bullseye
WORKDIR /app
COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt
COPY . .
CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]
Django:
FROM python:3.9-slim-bullseye
WORKDIR /app
COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt
COPY . .
CMD ["python3", "manage.py", "runserver", "0.0.0.0:8000"]
With Poetry, python package management like npm in Node:
pyproject.toml
similar topackage.json
in Nodepoetry.lock
similar topackage-lock.json
in Node
FROM python:3.9-slim-bullseye as builder
RUN pip install poetry
WORKDIR /app
COPY poetry.lock pyproject.toml ./
RUN poetry install
FROM python:3.9-slim-bullseye as base
WORKDIR /app
COPY --from=builder /app /app
ENV PATH="/app/.venv/bin:$PATH"
CMD ["python", "-m", "app.py"]
Dockerfile for Golang
Normal:
FROM golang:1.20-alpine AS build
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download && go mod verify
COPY . .
# Builds the application as a staticly linked one, to allow it to run on alpine
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o run .
# Moving the binary to the 'final Image' to make it smaller
FROM alpine
WORKDIR /app
COPY --from=build /build/run .
CMD ["/app/run"]
With private repo:
FROM golang:1.20-alpine AS build
# Install git and openssh
RUN apt update && apt upgrade -y && \
apt install -y git make openssh-client
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download && go mod verify
COPY . .
# Builds the application as a staticly linked one, to allow it to run on alpine
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o run .
# Moving the binary to the 'final Image' to make it smaller
FROM alpine
WORKDIR /app
COPY --from=build /build/run .
CMD ["/app/run"]
Dockerfile for Java Spring Boot
FROM eclipse-temurin:17-jdk-focal as build
WORKDIR /build
COPY .mvn/ ./.mvn
COPY mvnw pom.xml ./
RUN sed -i 's/\r$//' mvnw
RUN ./mvnw dependency:go-offline
COPY . .
RUN sed -i 's/\r$//' mvnw
RUN ./mvnw package -DskipTests
FROM eclipse-temurin:17-jdk-alpine
WORKDIR /app
COPY --from=build /build/target/*.jar run.jar
ENTRYPOINT ["java", "-jar", "/app/run.jar"]
Dockerfile for Java Quarkus
FROM maven:3.8.4-openjdk-17 AS build
WORKDIR /build
COPY ./pom.xml ./pom.xml
COPY ./settings.xml /root/.m2/settings.xml
RUN mvn dependency:go-offline -B
COPY src src
ARG QUARKUS_PROFILE
RUN mvn package -Dquarkus.profile=${QUARKUS_PROFILE}
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.4
ARG JAVA_PACKAGE=java-17-openjdk-headless
ARG RUN_JAVA_VERSION=1.3.8
ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en'
# Install java and the run-java script
RUN microdnf install curl ca-certificates wget ${JAVA_PACKAGE} \
&& microdnf update \
&& microdnf clean all \
&& mkdir /deployments \
&& chown 1001 /deployments \
&& chmod "g+rwX" /deployments \
&& chown 1001:root /deployments \
&& curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \
&& chown 1001 /deployments/run-java.sh \
&& chmod 540 /deployments/run-java.sh \
&& echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/conf/security/java.security
# We make four distinct layers so if there are application changes the library layers can be re-used
COPY --from=build /build/target/quarkus-app/lib/ /deployments/lib/
COPY --from=build /build/target/quarkus-app/*.jar /deployments/
COPY --from=build /build/target/quarkus-app/app/ /deployments/app/
COPY --from=build /build/target/quarkus-app/quarkus/ /deployments/quarkus/
USER 1001
ENTRYPOINT [ "/deployments/run-java.sh" ]
Dockerfile for ASP.NET Core
Normal:
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /build
# copy csproj and restore as distinct layers
COPY *.csproj .
RUN dotnet restore
# copy and publish app and libraries
COPY . .
RUN dotnet publish --no-restore -o app
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY --from=build /build/app .
ENTRYPOINT ["./aspnetapp"]
Alpine version:
FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build
WORKDIR /build
# copy csproj and restore as distinct layers
COPY *.csproj .
RUN dotnet restore
# copy and publish app and libraries
COPY . .
RUN dotnet publish --no-restore -o app
FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine
WORKDIR /app
COPY --from=build /build/app .
ENTRYPOINT ["./aspnetapp"]
Dockerfile for Ruby on Rails
Without assets:
FROM ruby:3.2-slim-bullseye
# Install system dependencies required both at runtime and build time
RUN apt-get update && apt-get install -y \
build-essential \
# example system dependencies that need for "gem install pg"
libpq-dev
COPY Gemfile Gemfile.lock ./
# Install (excluding development/test dependencies)
RUN gem install bundler && \
bundle config set without "development test" && \
bundle install
COPY . .
CMD ["rails", "server", "-b", "0.0.0.0"]
With assets:
FROM ruby:3.2-slim-bullseye
# Install system dependencies required both at runtime and build time
RUN apt-get update && apt-get install -y \
build-essential \
# example system dependencies that need for "gem install pg"
libpq-dev \
nodejs \
yarn
COPY Gemfile Gemfile.lock ./
# Install (excluding development/test dependencies)
RUN gem install bundler && \
bundle config set without "development test" && \
bundle install
COPY package.json yarn.lock ./
RUN yarn install
COPY . .
# Install assets
RUN RAILS_ENV=production SECRET_KEY_BASE=assets bundle exec rails assets:precompile
CMD ["rails", "server", "-b", "0.0.0.0"]
Note - On MacOS M Chip, maybe you need to add a flag --platform=linux/amd64
when build:
docker build . -t rubyonrails-app --platform=linux/amd64
Dockerfile for Dart
FROM dart AS build
WORKDIR /build
COPY pubspec.* /build
RUN dart pub get --no-precompile
COPY . .
RUN dart compile exe app.dart -o run
FROM debian:bullseye-slim
WORKDIR /build
COPY --from=build /build/run /app/run
CMD ["/app/run"]
Dockerfile for R Studio
SQL Server driver:
FROM rocker/rstudio
RUN apt-get update && apt-get install -y \
curl \
apt-transport-https \
tdsodbc \
libsqliteodbc \
gnupg \
unixodbc \
unixodbc-dev \
## clean up
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/ \
&& rm -rf /tmp/downloaded_packages/ /tmp/*.rds
RUN curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add - \
&& curl https://packages.microsoft.com/config/debian/9/prod.list > /etc/apt/sources.list.d/mssql-release.list \
&& apt-get update \
&& ACCEPT_EULA=Y apt-get install --yes --no-install-recommends msodbcsql17 msodbcsql18 mssql-tools18 \
&& install2.r odbc \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* \
&& rm -rf /tmp/*
RUN Rscript -e 'install.packages(c("DBI","odbc"))'
MYSQL driver:
FROM rocker/rstudio
RUN apt-get update && apt-get install -y \
curl \
apt-transport-https \
tdsodbc \
libsqliteodbc \
gnupg \
unixodbc \
unixodbc-dev \
## clean up
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/ \
&& rm -rf /tmp/downloaded_packages/ /tmp/*.rds
RUN wget https://downloads.mysql.com/archives/get/p/10/file/mysql-connector-odbc-8.0.19-linux-debian9-x86-64bit.tar.gz \
&& tar xvf mysql-connector-odbc-8.0.19-linux-debian9-x86-64bit.tar.gz \
&& cp mysql-connector-odbc-8.0.19-linux-debian9-x86-64bit/bin/* /usr/local/bin \
&& cp mysql-connector-odbc-8.0.19-linux-debian9-x86-64bit/lib/* /usr/local/lib \
&& sudo apt-get update \
&& apt-get install --yes libodbc1 odbcinst1debian2 \
&& chmod 777 /usr/local/lib/libmy*
RUN myodbc-installer -a -d -n "MySQL ODBC 8.0 Driver" -t "Driver=/usr/local/lib/libmyodbc8w.so" \
&& myodbc-installer -a -d -n "MySQL ODBC 8.0" -t "Driver=/usr/local/lib/libmyodbc8a.so"
RUN Rscript -e 'install.packages(c("DBI","odbc"))'