Deployment
Deploy your Nuxt application to production
This guide covers deploying your Nuxt 4 application to various hosting platforms.
Pre-deployment checklist
Before deploying to production:
- Set all required environment variables.
- Use production API keys (Stripe live keys, etc.).
- Configure production database.
- Set up Stripe webhooks for production URL.
- Test the application thoroughly.
- Set up error monitoring (Sentry, etc.).
- Configure analytics (Umami and/or Vercel Analytics).
- Review security settings.
- Set up SSL certificate.
Vercel (recommended)
Vercel provides the best experience for Nuxt applications with zero configuration.
Deployment steps
- Push to Git:
git push origin main
- Import project in Vercel:
- Go to vercel.com.
- Click "Add New Project".
- Import your Git repository.
- Vercel auto-detects Nuxt and configures build settings.
- Configure environment variables:
- In project settings → Environment Variables.
- Add all variables from your
.envfile. - Set appropriate scope (Production/Preview/Development).
- Deploy:
- Vercel automatically deploys on every push.
- View deployment at your
.vercel.appdomain.
Custom domain
- Go to project settings → Domains.
- Add your custom domain.
- Configure DNS records as instructed.
- SSL is automatically provisioned.
Build settings
Vercel uses these defaults (no configuration needed):
{
"buildCommand": "pnpm build",
"outputDirectory": ".output/public",
"installCommand": "pnpm install"
}
Netlify
Deployment steps
- Create
netlify.toml:
netlify.toml
[build]
publish = ".output/public"
command = "pnpm build"
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
[context.production.environment]
NUXT_PUBLIC_SITE_URL = "https://yourdomain.com"
- Connect to Netlify:
- Go to netlify.com.
- Import your Git repository.
- Configure build settings.
- Add environment variables.
- Deploy:
- Automatic deployment on Git push.
Cloudflare Pages
Deployment steps
- Connect repository:
- Go to Cloudflare dashboard.
- Create new Pages project.
- Connect Git repository.
- Build configuration:
- Framework preset: Nuxt.js.
- Build command:
pnpm build. - Build output directory:
.output/public.
- Environment variables:
- Add in project settings.
- Use separate variables for production/preview.
- Deploy:
- Automatic deployment on push.
Docker
Dockerfile
Dockerfile
# Build stage
FROM node:20-alpine AS build
WORKDIR /app
# Install pnpm
RUN corepack enable && corepack prepare pnpm@latest --activate
# Copy package files
COPY package.json pnpm-lock.yaml ./
# Install dependencies
RUN pnpm install --frozen-lockfile
# Copy source code
COPY . .
# Build application
RUN pnpm build
# Production stage
FROM node:20-alpine
WORKDIR /app
# Install pnpm
RUN corepack enable && corepack prepare pnpm@latest --activate
# Copy built application
COPY --from=build /app/.output ./.output
COPY --from=build /app/package.json ./
# Expose port
EXPOSE 3000
# Set environment
ENV NODE_ENV=production
ENV PORT=3000
# Start application
CMD ["node", ".output/server/index.mjs"]
docker-compose.yml
docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- '3000:3000'
environment:
- DATABASE_URL=${DATABASE_URL}
- NUXT_PUBLIC_SITE_URL=${NUXT_PUBLIC_SITE_URL}
- STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY}
depends_on:
- db
restart: unless-stopped
db:
image: postgres:16-alpine
environment:
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DB}
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
volumes:
postgres_data:
Build and run
# Build image
docker build -t myapp .
# Run container
docker run -p 3000:3000 \
-e DATABASE_URL="..." \
-e STRIPE_SECRET_KEY="..." \
myapp
# Or use docker-compose
docker-compose up -d
DigitalOcean App Platform
Deployment steps
- Create app:
- Go to DigitalOcean dashboard.
- Create App Platform app.
- Connect Git repository.
- Configure build:
- Build command:
pnpm build. - Run command:
node .output/server/index.mjs.
- Build command:
- Add environment variables:
- In app settings.
- Include all required variables.
- Add database (optional):
- Add PostgreSQL database component.
- Connection string auto-set in DATABASE_URL.
AWS (EC2)
Deployment steps
- Launch EC2 instance:
- Ubuntu 22.04 LTS.
- t3.small or larger.
- Configure security group (allow HTTP/HTTPS).
- Install dependencies:
# Connect to instance
ssh ubuntu@your-instance-ip
# Update system
sudo apt update && sudo apt upgrade -y
# Install Node.js
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
# Install pnpm
npm install -g pnpm
# Install PM2
npm install -g pm2
- Deploy application:
# Clone repository
git clone https://github.com/youruser/yourrepo.git
cd yourrepo
# Install dependencies
pnpm install
# Build application
pnpm build
# Start with PM2
pm2 start .output/server/index.mjs --name myapp
# Save PM2 configuration
pm2 save
pm2 startup
- Configure Nginx reverse proxy:
# /etc/nginx/sites-available/myapp
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
# Enable site
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
# Install SSL with Certbot
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com
Environment-specific configurations
Production
.env.production
NODE_ENV=production
NUXT_PUBLIC_SITE_URL="https://yourdomain.com"
DATABASE_URL="postgresql://prod-db:5432/myapp"
STRIPE_SECRET_KEY="sk_live_..."
STRIPE_WEBHOOK_SECRET="whsec_..."
# Analytics (optional)
NUXT_PUBLIC_UMAMI_ID="prod-website-id"
NUXT_PUBLIC_UMAMI_HOST="https://analytics.yourdomain.com"
NUXT_PUBLIC_VERCEL_ANALYTICS="true"
Staging
.env.staging
NODE_ENV=staging
NUXT_PUBLIC_SITE_URL="https://staging.yourdomain.com"
DATABASE_URL="postgresql://staging-db:5432/myapp_staging"
STRIPE_SECRET_KEY="sk_test_..."
# Analytics (optional)
NUXT_PUBLIC_UMAMI_ID="staging-website-id"
NUXT_PUBLIC_UMAMI_HOST="https://analytics.yourdomain.com"
Database migrations
Run Prisma migrations in production:
# In your CI/CD pipeline or deployment script
pnpm prisma migrate deploy
Never use
prisma migrate dev in production. Use migrate deploy instead.Health checks
Add a health check endpoint:
server/api/health.ts
export default defineEventHandler(async () => {
// Check database connection
try {
await prisma.$queryRaw`SELECT 1`
return {
status: 'healthy',
timestamp: new Date().toISOString(),
}
} catch (error) {
throw createError({
statusCode: 503,
message: 'Service unavailable',
})
}
})
Use in your monitoring:
curl https://yourdomain.com/api/health
Monitoring and logging
Error tracking (Sentry)
pnpm add @sentry/nuxt
nuxt.config.ts
export default defineNuxtConfig({
modules: ['@sentry/nuxt/module'],
sentry: {
dsn: process.env.SENTRY_DSN,
},
})
Application monitoring
- Umami Analytics: Privacy-focused web analytics (self-hosted or cloud).
- Vercel Analytics: Real-time traffic insights and Web Vitals.
- Datadog: Full-stack monitoring.
- New Relic: APM and monitoring.
- LogRocket: Session replay and monitoring.
See the analytics integration guide for Umami and Vercel Analytics setup.
Performance optimization
Enable caching
nuxt.config.ts
export default defineNuxtConfig({
nitro: {
compressPublicAssets: true,
routeRules: {
'/': { prerender: true },
'/api/**': { cors: true, headers: { 'cache-control': 's-maxage=60' } },
},
},
})
Image optimization
Use @nuxt/image for automatic optimization:
<NuxtImg src="/image.jpg" loading="lazy" format="webp" quality="80" />
Code splitting
Nuxt automatically code-splits by route. For large components:
<script setup>
const HeavyComponent = defineAsyncComponent(() => import('./HeavyComponent.vue'))
</script>
CI/CD
GitHub Actions
.github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: Run tests
run: pnpm test
- name: Build
run: pnpm build
env:
NUXT_PUBLIC_SITE_URL: ${{ secrets.SITE_URL }}
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.ORG_ID }}
vercel-project-id: ${{ secrets.PROJECT_ID }}
Troubleshooting
Build failures
Check Node.js version:
node --version # Should be 20.x or higher
Clear cache:
rm -rf .nuxt .output node_modules
pnpm install
pnpm build
Runtime errors
Check logs:
- Vercel: View in deployment logs.
- Docker:
docker logs container-name. - PM2:
pm2 logs myapp.
Check environment variables:
# Verify variables are set
env | grep NUXT_
Database connection errors
- Verify DATABASE_URL is correct.
- Check database is accessible from deployment.
- Verify SSL mode if required.
- Check firewall rules.
Post-deployment
After successful deployment:
- Test the application:
- Test all critical flows.
- Test authentication.
- Test payments (with test mode first).
- Configure monitoring:
- Set up error alerts.
- Configure uptime monitoring.
- Set up performance monitoring.
- Set up backups:
- Database backups.
- File storage backups.
- Documentation:
- Document deployment process.
- Document rollback procedure.
- Document emergency contacts.
Reference
Start with Vercel for the easiest deployment experience. You can always migrate to other platforms later if needed.