
Table of Contents
By Khimananda Oli | Last reviewed: August 2026
To Dockerize a Nuxt application effectively, you must move beyond basic tutorials and implement multi-stage builds that separate dependencies from runtime artifacts. This approach reduces image size by over 80% and eliminates build tools from your production attack surface, which is critical for compliance and security. Whether you are deploying to Kubernetes or a standalone VPS, following the patterns in this containerization fundamentals guide ensures your Nuxt app is portable, secure, and performant.
How do you write a production-ready Dockerfile to Dockerize a Nuxt application?
A common mistake when teams first Dockerize a Nuxt application is using a single-stage build that includes devDependencies, source code, and build tools in the final image. This results in 1GB+ images with unnecessary CVE exposure. The correct approach uses multi-stage builds to create a lean, secure artifact.
The Optimized Multi-Stage Dockerfile
This Dockerfile targets Nuxt 3.x with the Nitro server engine. It assumes you have a standard nuxt.config.ts and package.json in your project root.
# Stage 1: Build the application
FROM node:22-alpine AS builder
WORKDIR /app
# Copy dependency files first for layer caching
COPY package.json package-lock.json ./
# Install all dependencies including devDependencies needed for build
RUN npm ci
# Copy source code
COPY . .
# Build Nuxt - generates .output directory
RUN npm run build
# Stage 2: Production runtime
FROM node:22-alpine AS runtime
# Security: Create non-root user
RUN addgroup -g 1001 -S nuxtjs && \
adduser -S nuxtjs -u 1001 -G nuxtjs
WORKDIR /app
# Copy only the built output from builder stage
COPY --from=builder --chown=nuxtjs:nuxtjs /app/.output .output
# Set environment variables
ENV NODE_ENV=production
ENV HOST=0.0.0.0
ENV PORT=3000
# Switch to non-root user
USER nuxtjs
EXPOSE 3000
# Health check for orchestrators
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/ || exit 1
CMD ["node", ".output/server/index.mjs"] Several details here matter for production reliability. The npm ci command ensures deterministic installs matching your lockfile exactly, unlike npm install which may resolve newer versions. The --chown flag on COPY prevents permission errors when running as a non-root user. The HEALTHCHECK instruction allows Kubernetes or Docker Swarm to detect unresponsive containers automatically without external probes.
Handling Environment Variables Correctly
Nuxt handles environment variables differently at build time versus runtime. Variables prefixed with NUXT_PUBLIC_ are embedded during build and baked into the client bundle. Server-only variables like database credentials must be injected at runtime, never during build.
- Build-time:
NUXT_PUBLIC_API_BASE, feature flags, public metadata - Runtime:
DATABASE_URL,API_SECRET,REDIS_PASSWORD - Never embed: Secrets, tokens, or PII in the Docker image layers
For runtime secrets, integrate with Kubernetes secrets management or AWS Secrets Manager rather than baking them into environment files. This keeps your container images immutable and safe to promote across environments.
How do you optimize Docker image size when you Dockerize a Nuxt application?
Image size directly impacts deployment speed, cold start latency, and storage costs. A typical unoptimized Nuxt Docker image exceeds 900MB. With proper optimization, you can reduce this to under 150MB.
| Optimization Technique | Size Impact | Complexity | Trade-offs |
|---|---|---|---|
| Multi-stage build (alpine) | -70% to -80% | Low | Alpine uses musl libc; rare compatibility issues |
| Distroless base image | -85% to -90% | Medium | No shell for debugging; requires sidecar or ephemeral containers |
| .dockerignore exclusions | -5% to -15% | Low | Prevents cache invalidation from irrelevant file changes |
| Standalone Nitro preset | -10% to -20% | Low | Bundles only required node_modules into .output |
| Layer ordering optimization | 0% (build speed) | Low | Reduces rebuild time significantly in CI |
Using the Standalone Preset
Nuxt's Nitro server supports a standalone preset that traces actual dependencies and bundles them into the output directory. This eliminates the need to copy node_modules entirely in some configurations.
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
preset: 'node-server',
// Enable standalone output for minimal footprint
experimental: {
wasm: false
},
rollupConfig: {
output: {
// Ensures clean chunk splitting
manualChunks: undefined
}
}
}
}) When combined with the multi-stage Dockerfile above, the standalone preset ensures only actually-imported modules end up in the final image. Unused packages in package.json are excluded automatically.
Crafting an Effective .dockerignore
Your .dockerignore file prevents unnecessary context from being sent to the Docker daemon. This speeds up builds and prevents sensitive files from accidentally entering image layers.
# Version control
.git
.gitignore
# IDE and editor files
.vscode
.idea
*.swp
*.swo
# Documentation and non-production assets
README.md
LICENSE
docs/
*.md
# Test files
__tests__/
*.test.ts
*.spec.ts
coverage/
# Local environment and secrets
.env
.env.*
!.env.example
# Build artifacts (rebuilt in container)
.output
.nuxt
dist/
node_modules/
# OS and temp files
.DS_Store
Thumbs.db
*.log What are the security best practices when you Dockerize a Nuxt application?
Security is not optional for production containers. When you Dockerize a Nuxt application for enterprise or regulated environments, these practices are baseline requirements for SOC 2 and ISO 27001 compliance.
Non-Root User Enforcement
Running containers as root is a critical vulnerability. If an attacker exploits your Nuxt application, root access grants full container control and potential host escape vectors. Always create and switch to a dedicated non-root user as shown in the Dockerfile above.
Read-Only Filesystem
In Kubernetes, mount the root filesystem as read-only and use emptyDir volumes only for directories that require writes (like /tmp for Nitro cache). This prevents attackers from writing malicious scripts or modifying binaries post-exploitation.
# Kubernetes Pod Security Context example
securityContext:
runAsNonRoot: true
runAsUser: 1001
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL Image Scanning in CI
Integrate vulnerability scanning into your pipeline before pushing images. Tools like Trivy or Grype detect known CVEs in base images and dependencies. Block deployments if critical vulnerabilities are found. This aligns with the container image scanning practices used in mature DevSecOps workflows.
Minimal Base Images
Consider gcr.io/distroless/nodejs22-debian12 instead of Alpine for maximum security. Distroless images contain only the Node.js runtime and its direct dependencies—no shell, no package manager, no utilities. This makes exploitation significantly harder but requires adjustments to health checks and debugging workflows.
How do you deploy and run a Dockerized Nuxt application in production?
Building the image is only half the work. Running it reliably requires proper orchestration, networking, and observability configuration.
Docker Compose for Local and Staging
For local development or simple staging environments, Docker Compose provides a straightforward way to run your Nuxt container alongside databases and caches.
services:
nuxt-app:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- NUXT_PUBLIC_API_BASE=https://api.staging.example.com
- DATABASE_URL=postgresql://user:pass@db:5432/app
depends_on:
db:
condition: service_healthy
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/"]
interval: 30s
timeout: 3s
retries: 3
db:
image: postgres:16-alpine
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_DB: app
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user"]
interval: 5s
timeout: 3s
retries: 5
volumes:
pgdata: Kubernetes Deployment Considerations
For production Kubernetes deployments, reference the resource limits and requests guide to right-size your Nuxt pods. Nitro servers typically need 128-256Mi memory and 100-250m CPU for moderate traffic. Always set both requests and limits to prevent noisy-neighbor issues.
Configure readiness and liveness probes separately. The readiness probe should check your application's health endpoint (/api/health or similar), while the liveness probe can use a simpler TCP check on port 3000. This distinction prevents cascading restarts during temporary load spikes.
Monitoring and Observability
Containerized Nuxt applications require structured logging and metrics exposure. Configure Nitro to output JSON logs for parsing by Fluent Bit or Vector. Expose a /metrics endpoint using the nitro-prometheus module for scraping. Refer to the four golden signals guide to identify which metrics actually matter for your Nuxt workload: latency, traffic, errors, and saturation.
Deploy Your Dockerized Nuxt Application with Confidence
When you Dockerize a Nuxt application correctly, you gain reproducible deployments, improved security posture, and infrastructure portability across cloud providers. The multi-stage build pattern, non-root execution, and proper secret handling form the foundation of production-grade containerization. Start with the Dockerfile provided here, integrate image scanning into your CI pipeline, and validate your setup against the security benchmarks before going live. If you need help architecting a compliant, scalable Nuxt deployment or auditing your existing container setup, reach out to discuss your infrastructure needs.