Deployment - Producción
Llevar tu aplicación NextJS a producción implica preparar tu código, optimizarlo, y elegir dónde hospedarlo.
Build de producción
Antes de desplegar, crea un build optimizado:
npm run build
Este comando:
- Compila tu código TypeScript
- Optimiza imágenes
- Genera páginas estáticas
- Minimiza JavaScript y CSS
- Genera el bundle final
Output:
Route (app) Size First Load JS
┌ ○ / 137 B 85.9 kB
├ ○ /productos 2.34 kB 88.1 kB
├ ● /productos/[id] 1.89 kB 87.7 kB
└ ƒ /api/productos/route 0 B 0 B
○ (Static) Generada en build time
● (SSG) Pre-renderizada con generateStaticParams
ƒ (Dynamic) Renderizada on-demand
Leyenda:
- ○ Static: HTML estático, súper rápido
- ● SSG: Static Site Generation con datos
- ƒ Dynamic: Server-side rendering por request
Probar el build localmente
npm run build
npm run start
Abre http://localhost:3000
y prueba tu aplicación como estará en producción.
Siempre prueba el build antes de desplegar
npm run dev
es diferente a npm run build + npm start
. Siempre prueba el build de producción localmente antes de desplegar para detectar errores.
Preparación para producción
1. Variables de entorno
Crea un archivo .env.production
:
# .env.production
DATABASE_URL="postgresql://user:pass@host/db"
NEXT_PUBLIC_API_URL="https://api.mitienda.com"
SECRET_KEY="tu-secret-key-super-seguro"
Variables públicas vs privadas:
// ✅ Servidor (privada) - NO se expone al cliente
process.env.DATABASE_URL
process.env.SECRET_KEY
// ✅ Cliente (pública) - Se incluye en el bundle
process.env.NEXT_PUBLIC_API_URL
Nunca expongas secretos
Variables sin NEXT_PUBLIC_
son privadas y solo funcionan en el servidor. Úsalas para API keys, database URLs, y secrets.
2. Configuración de Next.js
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
// Modo estricto de React
reactStrictMode: true,
// Dominios permitidos para imágenes
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'cdn.mitienda.com',
},
],
},
// Redirecciones
async redirects() {
return [
{
source: '/old-route',
destination: '/new-route',
permanent: true,
},
]
},
// Headers de seguridad
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'X-Frame-Options',
value: 'DENY',
},
{
key: 'X-Content-Type-Options',
value: 'nosniff',
},
],
},
]
},
}
module.exports = nextConfig
3. Optimizar imágenes
Asegúrate de usar el componente Image
:
// ❌ Mal
<img src="/producto.jpg" alt="Producto" />
// ✅ Bien
import Image from 'next/image'
<Image
src="/producto.jpg"
alt="Producto"
width={500}
height={300}
priority // Para imágenes above-the-fold
/>
4. Revisar bundle size
npm run build
# Busca advertencias como:
# ⚠ Compiled with warnings
#
# ./app/page.tsx
# Module not found: Can't resolve 'huge-library'
Analizar bundle:
npm install @next/bundle-analyzer
# next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
})
module.exports = withBundleAnalyzer(nextConfig)
ANALYZE=true npm run build
Abre el reporte en el navegador y busca librerías grandes que puedas optimizar.
5. Configurar errores
// app/error.tsx
'use client'
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
<div>
<h2>Algo salió mal</h2>
<button onClick={reset}>Intentar de nuevo</button>
</div>
)
}
// app/not-found.tsx
export default function NotFound() {
return (
<div>
<h2>404 - Página no encontrada</h2>
<a href="/">Volver al inicio</a>
</div>
)
}
6. Robots.txt y Sitemap
// app/robots.ts
import { MetadataRoute } from 'next'
export default function robots(): MetadataRoute.Robots {
return {
rules: {
userAgent: '*',
allow: '/',
disallow: '/admin/',
},
sitemap: 'https://mitienda.com/sitemap.xml',
}
}
// app/sitemap.ts
import { MetadataRoute } from 'next'
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const productos = await fetch('https://api.mitienda.com/productos')
.then(r => r.json())
return [
{
url: 'https://mitienda.com',
lastModified: new Date(),
priority: 1,
},
...productos.map((p) => ({
url: `https://mitienda.com/productos/${p.id}`,
lastModified: p.updatedAt,
priority: 0.8,
})),
]
}
Opciones de hosting
1. Vercel (recomendado)
Ventajas:
- ✅ Deploy automático desde Git
- ✅ Preview deployments
- ✅ Edge functions
- ✅ Analytics incluido
- ✅ Zero-config
Ideal para:
- Cualquier app NextJS
- Equipos pequeños y medianos
- Prototipado rápido
Precio:
- Gratis para hobby
- Pro: $20/mes por usuario
2. Netlify
Ventajas:
- ✅ Deploy automático
- ✅ Formularios integrados
- ✅ Functions serverless
Configuración:
# netlify.toml
[build]
command = "npm run build"
publish = ".next"
[[plugins]]
package = "@netlify/plugin-nextjs"
3. AWS (Amplify)
Ventajas:
- ✅ Integración con servicios AWS
- ✅ CDN global
- ✅ Escalable
Para:
- Apps enterprise
- Necesitas integración AWS
4. Docker (self-hosting)
Dockerfile:
FROM node:18-alpine AS base
FROM base AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT 3000
CMD ["node", "server.js"]
// next.config.js
module.exports = {
output: 'standalone',
}
docker build -t mi-app .
docker run -p 3000:3000 mi-app
5. VPS (DigitalOcean, Linode)
Setup básico:
# En tu servidor
git clone https://github.com/tuusuario/tu-app
cd tu-app
npm install
npm run build
# Usar PM2 para mantener la app corriendo
npm install -g pm2
pm2 start npm --name "mi-app" -- start
pm2 save
pm2 startup
Nginx como reverse proxy:
# /etc/nginx/sites-available/mi-app
server {
listen 80;
server_name mitienda.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;
}
}
6. Static Export
Para sitios 100% estáticos (sin servidor):
// next.config.js
module.exports = {
output: 'export',
}
Ideal para:
- Blogs
- Landing pages
- Documentación
- Portfolios
→ Guía completa de Static Exports
Comparación de opciones
Opción | Facilidad | Precio | Escalabilidad | Server Components | Server Actions |
---|---|---|---|---|---|
Vercel | ⭐⭐⭐⭐⭐ | $0-$20 | ⭐⭐⭐⭐⭐ | ✅ | ✅ |
Netlify | ⭐⭐⭐⭐⭐ | $0-$19 | ⭐⭐⭐⭐ | ✅ | ✅ |
AWS | ⭐⭐⭐ | Variable | ⭐⭐⭐⭐⭐ | ✅ | ✅ |
Docker | ⭐⭐ | Depende | ⭐⭐⭐⭐ | ✅ | ✅ |
VPS | ⭐⭐ | $5-$50 | ⭐⭐⭐ | ✅ | ✅ |
Static | ⭐⭐⭐⭐⭐ | $0 | ⭐⭐⭐ | ❌ | ❌ |
Monitoreo y analytics
Vercel Analytics
// app/layout.tsx
import { Analytics } from '@vercel/analytics/react'
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Analytics />
</body>
</html>
)
}
Vercel Speed Insights
import { SpeedInsights } from '@vercel/speed-insights/next'
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<SpeedInsights />
</body>
</html>
)
}
Sentry (errores)
npm install @sentry/nextjs
npx @sentry/wizard@latest -i nextjs
// sentry.client.config.ts
import * as Sentry from '@sentry/nextjs'
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
tracesSampleRate: 1.0,
})
Checklist de producción
Antes de desplegar:
Performance:
- Ejecuta
npm run build
sin errores - Páginas críticas < 100 KB First Load JS
- Imágenes optimizadas con
<Image>
- Fonts optimizadas con
next/font
SEO:
- Metadata en todas las páginas
- Sitemap.xml configurado
- Robots.txt configurado
- Open Graph images
Seguridad:
- Variables de entorno configuradas
- API keys en .env, no hardcodeadas
- Headers de seguridad configurados
- HTTPS activado
Funcionalidad:
- Páginas de error (404, 500)
- Loading states
- Manejo de errores
- Pruebas E2E pasando
Monitoreo:
- Analytics configurado
- Error tracking (Sentry)
- Speed insights
Variables de entorno en producción
Vercel
# CLI
vercel env add NOMBRE_VARIABLE
# O en dashboard: Settings → Environment Variables
Netlify
# CLI
netlify env:set NOMBRE_VARIABLE valor
# O en dashboard: Site settings → Environment variables
Docker
docker run -e DATABASE_URL="..." -e SECRET_KEY="..." mi-app
Dominios personalizados
Vercel
- Ve a Settings → Domains
- Agrega tu dominio
- Configura DNS:
Type: A
Name: @
Value: 76.76.21.21
Type: CNAME
Name: www
Value: cname.vercel-dns.com
Cloudflare (proxy)
Si usas Cloudflare:
Type: CNAME
Name: @
Value: cname.vercel-dns.com
Proxy: ON (naranja)
CI/CD
GitHub Actions
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 18
- name: Install
run: npm ci
- name: Build
run: npm run build
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
- name: Deploy
run: npx vercel --prod
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
Troubleshooting
Build falla
# Error: Module not found
→ Verifica imports y paths
# Error: Out of memory
→ Aumenta memoria: NODE_OPTIONS="--max-old-space-size=4096"
# Error: Type errors
→ Ejecuta: npm run type-check
Performance lenta
# Analiza bundle
ANALYZE=true npm run build
# Revisa métricas en Vercel Speed Insights
# Lighthouse en Chrome DevTools
Imágenes no cargan
// Verifica dominios permitidos
// next.config.js
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'tu-cdn.com',
},
],
}
Mejores prácticas
1. Usa preview deployments
# Vercel crea preview por cada PR
# Prueba cambios antes de mergear
2. Monitorea errores
// Sentry captura errores automáticamente
// Revisa dashboard regularmente
3. Optimiza continuamente
# Lighthouse CI en cada deploy
# Mantén score > 90
4. Cache agresivamente
// Revalidación apropiada
export const revalidate = 3600 // 1 hora
5. Backups de base de datos
# Automatiza backups diarios
# Prueba restore regularmente
Resumen
Para llevar a producción:
npm run build
- Crea build optimizado- Prueba localmente con
npm start
- Configura variables de entorno
- Elige hosting (Vercel recomendado)
- Configura dominio personalizado
- Activa monitoreo
Hosting recomendado:
- Vercel: Mejor opción para mayoría de casos
- Netlify: Alternativa sólida
- Docker/VPS: Para control total
- Static: Para sitios sin servidor
Próximos pasos:
- Deploy en Vercel - Paso a paso
- Static Exports - Sitios estáticos