Docker is a very useful tool, but facing a blank file for the first time can be intimidating. Fortunately, one of its advantages is that you don’t have to reinvent the wheel every time you start a project.
In this article, I have compiled 10 practical Dockerfile examples covering everything from basic system configurations to advanced Multi-stage architectures for compiled languages.
Use them as base templates, copy them and adapt them to the needs of your own projects 👇.
Utilities and Basic Configuration
Ideal for creating isolated command-line tools or understanding how environment variables work.
Base Image (Ubuntu + Curl)
This example illustrates how to start from a blank operating system and install basic Linux packages.
FROM ubuntu:22.04
# Update repositories and install curl
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
# Default command
CMD ["curl", "--version"]
Using Environment Variables (httpie)
Learn to use the ENV instruction to make your container dynamic and accept parameters at startup (e.g., docker run -e URL=[https://google.com](https://google.com) my-image).
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y httpie && rm -rf /var/lib/apt/lists/*
# Define a variable with default value
ENV URL=https://example.com
# Use the variable in execution
CMD ["sh", "-c", "http -h $URL"]
Interpreted Languages
Classic web applications usually require copying dependencies, installing them, and then copying the source code.
Node.js (Express / Frontend)
The industry standard for JavaScript applications. We copy the package.json before the code to leverage Docker’s cache.
FROM node:18-alpine
WORKDIR /app
# Install dependencies first
COPY package*.json ./
RUN npm install
# Copy the rest of the code
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
Python (Django / Flask)
Prepared to install dependencies from a requirements.txt file without caching garbage that would bloat the image.
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["python", "app.py"]
PHP with Apache
A very quick setup for serving legacy projects or classic PHP architectures.
FROM php:8.1-apache
# Enable mod_rewrite for friendly URLs
RUN a2enmod rewrite
# Copy code directly to Apache's public folder
COPY src/ /var/www/html/
# Adjust permissions
RUN chown -R www-data:www-data /var/www/html
EXPOSE 80
# CMD is already configured in the Apache base image
Ruby on Rails
A more complex environment that requires precompiling assets and managing system tools before startup.
FROM ruby:3.2-slim
WORKDIR /app
# Install system dependencies required for Rails
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
COPY Gemfile Gemfile.lock ./
RUN bundle install
COPY . .
# Precompile Rails assets
RUN bundle exec rake assets:precompile
EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0"]
Compiled Languages
In languages like Java, Go, or .NET, we use a heavy image to compile the code, and then copy only the final executable to an empty, ultra-light image for production.
.NET (C# / ASP.NET)
Microsoft’s recommended way to package enterprise applications.
# STAGE 1: Build and Publish
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /source
COPY *.csproj .
RUN dotnet restore
COPY . .
RUN dotnet publish -c Release -o /app/publish
# STAGE 2: Production (Runtime only)
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS runtime
WORKDIR /app
COPY --from=build /app/publish .
EXPOSE 80
ENTRYPOINT ["dotnet", "your-application.dll"]
Java (Spring Boot with Maven)
We compile the .jar file using Maven in the first stage and run it using a lightweight Java environment (JRE) in the second stage.
# STAGE 1: Compilation
FROM maven:3.8-eclipse-temurin-17 AS build
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests
# STAGE 2: Production
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]
Go (Golang)
We go from a compilation image of several hundred megabytes to a final production image that takes up just a few MB.
# STAGE 1: Compilation
FROM golang:1.20-alpine AS build
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -o main_app
# STAGE 2: Production
FROM alpine:latest
WORKDIR /app
# Copy only the compiled binary
COPY --from=build /app/main_app .
EXPOSE 8080
CMD ["./main_app"]
Rust
Useful for high-performance microservices where the final image must be minimal and secure (it’s safe!! 🦀).
# STAGE 1: Compilation
FROM rust:1.70 AS build
WORKDIR /app
COPY Cargo.toml Cargo.lock ./
# Trick to cache dependencies by compiling an empty project
RUN mkdir src && echo "fn main() {}" > src/main.rs
RUN cargo build --release
RUN rm -rf src
# Compile the real code
COPY src ./src
RUN cargo build --release
# STAGE 2: Production
FROM debian:bullseye-slim
WORKDIR /app
COPY --from=build /app/target/release/mi_app /usr/local/bin/
EXPOSE 8000
CMD ["mi_app"]
