Dockerizing Nuxt Image

How to dockerize Nuxt 3 applications?

A practical walkthrough of Dockerizing a Nuxt 3 application: learn step-by-step how to create a Nuxt project, implement multi-stage builds, optimize Docker images, and set up Docker Compose and NGINX production build.

7 min read

December 18th, 2024

Nick Mousavi

Introduction

Remember when front-end developers only worried about making websites look good? Those days are gone. Now, frameworks like Nuxt and Next aren't just about pretty interfaces they're full-stack powerhouses that require us to understand way more than just HTML and CSS.

Dockerizing your application has become a must-have skill, and it's not as scary as it sounds. In this friendly guide, we'll break down the process of creating a Nuxt app and turning it into a Docker container.

Requirements

  • An existing Nuxt application for dockerization, or create a new Nuxt application.
  • Ensure Docker and Docker Compose are installed on you machine by running the following commands:
    docker version 
    docker compose version
    

In this guide, I use npm as package manager, you can use any other that you want.

Note: docker-compose is the older, standalone Python-based CLI tool that was originally developed as a separate project.

Old way: docker-compose version

New way: docker compose version

Create Dockerfile

At the root of your project, right next to the nuxt.config.ts file, go ahead and create a Dockerfile. Dockerfile and Nuxt Config Image Now we can include our instruction that Docker can use to package our application into an image:

Dockerfile
# Use Node.js Alpine for build stage with all dev tools
FROM node:22.12.0-alpine AS build

# Set working directory inside the container
WORKDIR /app

# Copy package files for dependency installation
COPY package*.json ./

# Install dependencies using clean install for reproducibility
RUN npm ci

# Copy entire project to container
COPY . .

# Build the Nuxt application
RUN npm run build

# Use distroless Node.js image for production for minimal, secure runtime
FROM gcr.io/distroless/nodejs22-debian12 AS prod

# Set working directory in production container
WORKDIR /app

# Copy built output from build stage to production container
COPY --from=build /app/.output ./.output

# Expose port the app will run on
EXPOSE 3000

# Command to start the Nuxt server from built output
CMD ["./.output/server/index.mjs"]

Create .dockerignore (Optional)

It's highly recommended to create .dockerignore file to ignore unnecessary files in your Docker Image. it's very similar to .gitignore or .eslintignore.

just next to your Dockerfile, create a file named .dockerignore and add the following content:

node_modules
.output
.nuxt
.git

Build and Run Docker Image

Run the following command to build your Docker image:

docker build -t nuxt-docker .
  • -t nuxt-docker: This flag tags (names) your Docker image as "nuxt-docker", making it easier to reference later
  • . tells Docker to use the current directory's Dockerfile as the build context, meaning it will look for the Dockerfile in your current working directory

After building your Docker image, run the following command to start the container:

docker run -p 3000:3000 nuxt-docker
  • -p 3000:3000 maps the container's port 3000 to your local machine's port 3000
  • This allows you to access the Nuxt application in your browser at http://localhost:3000
  • You can make sure your 3000 port is free before running your container: npx kill-port 3000

Now you can view your Nuxt application running inside a Docker container!

Advanced Command (Optional)

The following command is more practical and advanced to run you docker container.

docker run -d --name nuxt-app -p 3000:3000 --restart unless-stopped nuxt-docker
  • -d: Runs the container in detached mode (background mode)
  • --name nuxt-app: Gives a meaningful name to your container for easier management
  • --restart unless-stopped: Automatically restarts the container if it crashes or when Docker restarts, unless you manually stop it

Production Build (Docker Compose and NGINX)

This section is optional. If you've already run your Docker container successfully, you can skip it.

However, Docker Compose becomes incredibly useful when you want to orchestrate multiple services, like combining your Nuxt app with a database or backend service, all working together in a single configuration.

Think of Docker Compose as a conductor, managing multiple containers and their interactions with just one command, making complex setups a breeze.

We also use Replica feature in Docker Compose to scale the app horizontally and using NGINX upstream for load balancing.

Create docker-compose.yaml next to the Dockerfile and add the following content:

file_type_light_yaml docker-compose.yaml
services:
  nuxt:
    build:
      context: .
      dockerfile: Dockerfile
      target: prod
    deploy:
      replicas: 3
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
    ports:
      - "3000-3002:3000"
    networks:
      - web-network
    environment:
      - NODE_ENV=production
      - PORT=3000
    healthcheck:
      test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://localhost:3000"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    networks:
      - web-network
    depends_on:
      - nuxt

networks:
  web-network:
    driver: bridge

last but not least, you need to create nginx.conf file, again, next to the Dockerfile and add the following content:

events {
    worker_connections 1024;
}

http {
    upstream nuxt-cluster {
        least_conn;
        server nuxt:3000;
        server nuxt:3001;
        server nuxt:3002;
    }

    server {
        listen 80;

        location / {
            proxy_pass http://nuxt-cluster;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
}

Build and start the containers

docker compose up --build -d

Check running containers

docker compose ps

Verify the Result

curl http://localhost
# Or open http://localhost in your browser

Conclusion

By leveraging Docker and NGINX, you transform application deployment into a scalable infrastructure. This approach provides horizontal scaling, consistent environments, enhanced security, and improved developer experience. Containerization eliminates "it works on my machine" problems while ensuring high availability and performance.

If you found this guide helpful, please ⭐ the repository - your support means a lot!

Docker and Nuxt.js: Quick Q&A

NGINX acts as a powerful reverse proxy and load balancer, efficiently distributing incoming traffic across multiple Nuxt.js application instances. It handles routing, can implement caching, manages SSL termination, and provides an additional layer of security and performance optimization.

NGINX enables horizontal scaling by intelligently distributing requests across multiple container replicas. It supports advanced load balancing algorithms (like least connections), provides health checks to route traffic only to healthy containers, and can dynamically adapt to changes in your application's infrastructure.

Docker is a platform that allows you to package, distribute, and run applications in isolated environments called containers. It simplifies deployment by ensuring consistency across different development and production systems.

Multi-stage builds let you create more efficient Docker images by using multiple FROM statements. Each stage can copy specific artifacts from previous stages, resulting in smaller final images with only necessary components.

A container is a lightweight, standalone, executable package that includes everything needed to run an application: code, runtime, system tools, libraries, and settings. It's essentially a running instance of a Docker image.

Docker provides consistent environments, simplifies deployment, enables easy scaling, and isolates your Nuxt.js application. It helps manage dependencies, ensures identical setups across different machines, and supports seamless continuous integration.

Distroless images are minimal container images that contain only your application and its runtime dependencies, without package managers, shells, or other system tools. They improve security by reducing the attack surface and image size.

A replica is a copy of a container running the same service. In Docker Compose or Swarm, replicas allow you to scale applications horizontally, distribute load, and provide high availability by running multiple identical instances.

Docker standardizes deployment by packaging applications with all dependencies, enables quick scaling, supports microservices architecture, and provides consistent environments from development to production.

Yes, Docker can improve Nuxt.js performance through efficient container management, easy horizontal scaling, load balancing, and simplified deployment of optimized production builds.

By copying package files first and installing dependencies before copying the full project, you leverage Docker's layer caching. This means if your package.json hasn't changed, Docker can reuse the cached dependency layer, making subsequent builds much faster. It's a performance optimization that prevents reinstalling packages on every build when your code changes.

Resources and References

Github Repository

NGINX upstream

Docker

Docker Compose

Nuxt 3 Docker Image Optimization

Dockerizing a Nuxt App

Nuxt 3 Docker starter

Nick Mousavi
A small step forward is still progress.

To get in touch with Nick Mousavi,
please enter your email address below.

© 2020-present Nick Mousavi. All Rights Reserved.

Privacy Policy