Docker for Web Applications: Deploying Next.js in Containers
To effectively deploy the Media Jet web application, based on Next.js, an architecture founded on Docker containerization technology was chosen. This approach allows for the isolation of the application and all its dependencies, ensuring stability and portability across different environments. The orchestration of all system components is managed using Docker Compose.
Containerization Architecture for the Media Jet Project
| Service | Purpose |
|---|---|
| Next.js (web) | The main web application responsible for the user interface. |
| PostgreSQL (db) | Relational database for persistent data storage. |
| Redis (redis) | In-memory storage for caching sessions and data. |
| MinIO (minio) | S3-compatible storage for media files and static content. |
To effectively deploy the Media Jet web application, based on Next.js, an architecture founded on Docker containerization technology was chosen. This approach allows for the isolation of the application and all its dependencies, ensuring stability and portability across different environments. The orchestration of all system components is managed using Docker Compose.
The core element of the system is the Next.js application itself. A PostgreSQL database is used for data storage, the high-performance Redis store is used for caching tasks, and the S3-compatible object storage MinIO is used for storing media files. All these components operate as separate but interconnected services.
Creating an Efficient Dockerfile for Next.js
The key to creating a lightweight and secure Docker image for a Next.js application lies in using a multi-stage build. This approach allows the process to be split into two independent stages: building and running, which prevents unnecessary tools and source code from ending up in the final image.
The first stage, called 'builder', is used to prepare the application. It is based on the node:18-alpine image, which includes all the necessary build tools. In this stage, dependencies are installed using the fast package manager pnpm, after which the `next build` command is executed to create the production version of the application.
The second stage, 'runner', is responsible for running the application in production. It uses the same minimalistic node:18-alpine image. Only the necessary files are copied from the 'builder' stage: the `.next` build folder, the `public` static folder, and `package.json`. This excludes the heavy `node_modules` and source code, ensuring a minimal size and enhanced security. The application is started with the `next start` command.

Key Techniques for Docker Image Optimization
Proper Docker image optimization is a critical aspect for speeding up deployment and reducing storage costs. Applying several key techniques has led to impressive results, reducing the final image size from 2 GB to just 200 MB.
The main contribution to optimization comes from the multi-stage build, which separates the build environment from the runtime environment. Other important methods include:
- Using .dockerignore: This file, similar to .gitignore, prevents unnecessary files and folders from being copied into the Docker build context. It is essential to include `node_modules`, the local `.next` cache, and other temporary files to ensure they don't end up in the image.
- Caching Docker layers: Docker builds images in layers, caching the result of each instruction. To use this effectively, the dependency installation instructions (`COPY package.json` and `pnpm install`) should be placed before the `COPY . .` instruction. This way, if the dependencies haven't changed, Docker will use the cached layer, significantly speeding up subsequent builds.

Orchestrating Services with Docker Compose
Docker Compose is a tool for defining and running multi-container Docker applications. Using a single YAML file (docker-compose.yml), the entire project infrastructure is described, including services, networks, and volumes for data storage.
For the Media Jet project, four main services are defined: `web` (Next.js), `db` (PostgreSQL), `redis`, and `minio`. Each service runs in its own container, but they can interact with each other through an isolated virtual network created by Docker Compose. This ensures both security and ease of configuration.
To ensure data persistence, which should not be lost when containers are restarted, volumes are used. They are attached to the PostgreSQL container for storing the database and to the MinIO container for storing uploaded media files. This way, data remains safe even if the containers are completely removed and recreated.

Deployment, Updates, and Scaling
Managing the application lifecycle with Docker Compose becomes a simple and predictable process. All operations are performed using a few commands, which significantly reduces the complexity of server administration.
The deployment and management process is as follows:
- Deployment: To start the entire application stack on the server for the first time, simply run the `docker-compose up` command. This command will automatically download or build the images, create networks and volumes, and start all services.
- Updating: To update the application to a new version, first pull the latest image with the `docker-compose pull` command. After that, the `docker-compose up -d` command will recreate only the containers whose images have changed, ensuring an update with virtually no downtime.
- Scaling: As traffic increases, you can easily increase the number of web application instances. Docker Compose allows you to scale any service, for example, by increasing the number of replicas for the `web` service to distribute the load.

Container Monitoring and Security
Ensuring the stable operation and security of a containerized application requires implementing monitoring practices and adhering to precautionary measures. Docker provides built-in tools for basic control, and best practices help strengthen security.
- Logging: Access to the logs of all running services can be obtained centrally via the `docker-compose logs` command.
- Health Checks: A `HEALTHCHECK` instruction can be added to the Dockerfile, allowing Docker to periodically check the application's health inside the container.
- Metrics: For more advanced monitoring, a metrics collection system like Prometheus can be optionally integrated.
- Using a non-root user: Running processes in the container as a user with limited privileges reduces potential damage in case of a compromise.
- Secret management: Sensitive data, such as passwords and API keys, should be passed to containers via environment variables rather than being embedded in the image.
- Updating base images: Regularly updating base images (e.g., `node:18-alpine`) helps to receive timely security patches.

Results and Performance Metrics
| Metric | Value |
|---|---|
| Image Size | 200 MB (down from 2 GB without optimization) |
| Build Time | ~5 minutes |
| Stack Startup Time | ~10 seconds |
The implementation of containerization using Docker and smart optimization techniques brought measurable and significant improvements to the development and deployment process of the Media Jet application. The results demonstrate the high efficiency of the chosen approach.
A comparison of key metrics before and after optimization clearly shows the benefits. The most significant achievement was the radical reduction in the Docker image size, which directly impacts deployment speed and storage costs.
These metrics confirm that containerizing Next.js applications with multi-stage builds and orchestration via Docker Compose is a powerful solution that provides speed, efficiency, and reliability throughout all stages of the product lifecycle.
