A Dockerfile is a text file that contains a set of instructions to build a Docker image. It defines the environment in which your application will run, including the base image, dependencies, and configuration settings. A good Dockerfile should be efficient, maintainable, and easy to understand. Here are some best practices for writing a good Dockerfile:
- Use a small base image: Start with a minimal base image to reduce the size of your final image. For example, use
alpine
or scratch
instead of ubuntu
or debian
if possible. - Use multi-stage builds: If your application requires a build step, use multi-stage builds to separate the build environment from the runtime environment. This helps to keep the final image size small and reduces the attack surface.
- Minimize the number of layers: Each instruction in a Dockerfile creates a new layer in the image. Combine related instructions into a single
RUN
command to reduce the number of layers and improve build performance. - Use
.dockerignore
: Create a .dockerignore
file to exclude unnecessary files and directories from being copied into the image. This helps to reduce the image size and build time. - Use environment variables: Use environment variables to configure your application and avoid hardcoding values in the Dockerfile. This makes it easier to change configurations without modifying the Dockerfile.
- Use explicit versioning: Specify the exact version of dependencies in your Dockerfile to ensure consistent builds. Avoid using
latest
tags, as they can lead to unexpected changes in your application behavior. - Use COPY instead of ADD: Use the
COPY
instruction to copy files into the image instead of ADD
, unless you need to extract a tar file or download a remote file. COPY
is more explicit and avoids unnecessary complexity. - Clean up after installation: Remove unnecessary files and dependencies after installing packages to keep the image size small. Use
apt-get clean
or similar commands to clean up package manager caches. - Use health checks: Add a
HEALTHCHECK
instruction to your Dockerfile to monitor the health of your application. This helps to ensure that your application is running correctly and can be restarted if necessary. - Document your Dockerfile: Add comments to explain the purpose of each instruction in your Dockerfile. This makes it easier for others (and yourself) to understand the build process and maintain the Dockerfile in the future.
- Use a specific user: Avoid running your application as the root user inside the container. Create a non-root user and switch to that user using the
USER
instruction. This improves security and reduces the risk of privilege escalation attacks. - Use a specific working directory, entry point: Set a specific working directory using the
WORKDIR
instruction. This helps to keep your application files organized and makes it easier to run commands inside the container.
1. Dockerfile NodeJS
FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
FROM node:18-alpine
WORKDIR /app
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app .
ENV NODE_ENV=production
ENV PORT=3000
EXPOSE 3000
CMD ["node", "server.js"]
FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
FROM node:18-alpine
RUN npm install -g pm2
WORKDIR /app
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app .
ENV NODE_ENV=production
ENV PORT=3000
EXPOSE 3000
CMD ["pm2-runtime", "start", "server.js"]
2. Dockerfile NestJS
FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:18-alpine
WORKDIR /app
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/main".js"]
3. Dockerfile Express.js
FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
FROM node:18-alpine
WORKDIR /app
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app .
EXPOSE 3000
CMD ["node", "index.js"]
4. Dockerfile React.js
Dockerfile
nginx/nginx.conf
FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 3000
CMD ["nginx", "-g", "daemon off;"]
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
server_tokens off;
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
server {
listen 3000; # Lắng nghe trên cổng 3000
server_name localhost;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, no-transform";
}
error_page 404 /404.html;
location = /404.html {
internal;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
internal;
}
location ~ /\.ht {
deny all;
}
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy "no-referrer-when-downgrade";
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'self';";
}
}
5. Vite and Nginx
FROM node:21-alpine3.18 as build
WORKDIR /app
COPY package.json ./
COPY package-lock.json ./
RUN npm install
COPY . .
RUN npm run build
FROM nginx:1.25-alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri /index.html;
}
}
6. Dockerfile Go
FROM golang:1.20-alpine AS build
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/myapp
FROM alpine:latest
WORKDIR /app
COPY --from=build /app/myapp /app/myapp
EXPOSE 8080
CMD ["/app/myapp"]
7. Dockerfile Python
FROM python:3.9-slim AS build
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
COPY . .
FROM python:3.9-slim
WORKDIR /app
COPY --from=build /root/.local /root/.local
COPY --from=build /app .
ENV PATH=/root/.local/bin:$PATH
EXPOSE 5000
CMD ["flask", "run", "--host=0.0.0.0"]
FROM python:3.9-slim AS build
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
COPY . .
FROM python:3.9-slim
WORKDIR /app
COPY --from=build /root/.local /root/.local
COPY --from=build /app .
ENV PATH=/root/.local/bin:$PATH
EXPOSE 8000
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
8. Dockerfile NetCore
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
COPY *.csproj .
RUN dotnet restore
COPY . .
RUN dotnet publish -c Release -o /app/publish /p:UseAppHost=false
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS runtime
WORKDIR /app
COPY --from=build /app/publish .
ENV ASPNETCORE_URLS=http://+:80
ENV DOTNET_RUNNING_IN_CONTAINER=true
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
RUN find /app -name "*.pdb" -delete && \
find /app -name "*.xml" -delete
EXPOSE 80
ENTRYPOINT ["dotnet", "YourApp.dll"]