next-standalone-env
v1.0.1
Published
Runtime environment variable resolution for Next.js standalone/Docker deployments. Allows the same Docker image to work across multiple environments.
Maintainers
Readme
next-standalone-env
Runtime environment variable resolution for Next.js standalone/Docker deployments
The Problem
When Next.js builds in standalone mode (used for Docker deployments), environment variables like NEXTAUTH_URL are baked in at build time. This means:
- ❌ You need separate Docker images for dev, staging, and production
- ❌ You can't use the same image across environments
- ❌ OAuth callbacks fail when URLs don't match
- ❌ You have to rebuild for every environment change
The Solution
next-standalone-env patches environment variables at runtime when your container starts, allowing:
- ✅ One Docker image for all environments
- ✅ Runtime configuration via
APP_ENV - ✅ Proper OAuth redirects in every environment
- ✅ Build once, deploy everywhere
Installation
npm install next-standalone-env
# or
yarn add next-standalone-env
# or
pnpm add next-standalone-envQuick Start
1. Simple Usage (Recommended)
Replace your Next.js standalone server with our runtime-aware server:
# In your Dockerfile
FROM node:20-alpine
# ... your build steps ...
# Copy the runtime server
COPY --from=builder /app/node_modules/next-standalone-env/server.js ./server.js
# Use our server instead of Next.js standalone server
CMD ["node", "server.js"]Set your environment:
# Development
docker run -e APP_ENV=development your-image
# Production
docker run -e APP_ENV=production your-image
# Staging
docker run -e APP_ENV=staging your-image2. Custom Configuration
Create runtime-env.config.js in your project root:
module.exports = {
environments: {
production: {
vars: {
NEXTAUTH_URL: 'https://app.example.com',
NEXT_PUBLIC_API_URL: 'https://api.example.com'
}
},
development: {
vars: {
NEXTAUTH_URL: 'https://dev.example.com',
NEXT_PUBLIC_API_URL: 'https://api-dev.example.com'
}
},
staging: {
vars: {
NEXTAUTH_URL: 'https://staging.example.com',
NEXT_PUBLIC_API_URL: 'https://api-staging.example.com'
}
}
},
// Variables that can be overridden at runtime
variables: ['NEXTAUTH_URL', 'NEXT_PUBLIC_API_URL', 'DATABASE_URL'],
// The env var that determines which environment to use
envSelector: 'APP_ENV', // or 'NODE_ENV', 'ENVIRONMENT', etc.
// Enable debug logging
debug: true
};3. NextAuth Integration
Your NextAuth configuration works seamlessly:
// app/api/auth/[...nextauth]/route.js
import NextAuth from 'next-auth';
import AzureADProvider from 'next-auth/providers/azure-ad';
const handler = NextAuth({
providers: [
AzureADProvider({
clientId: process.env.AZURE_AD_CLIENT_ID,
clientSecret: process.env.AZURE_AD_CLIENT_SECRET,
tenantId: process.env.AZURE_AD_TENANT_ID,
}),
],
// NEXTAUTH_URL is now set correctly at runtime!
});
export { handler as GET, handler as POST };Advanced Usage
Programmatic API
import { patchProcessEnv } from 'next-standalone-env';
// In your custom server
patchProcessEnv({
environments: {
production: {
vars: { NEXTAUTH_URL: 'https://app.example.com' }
},
development: {
vars: { NEXTAUTH_URL: 'https://dev.example.com' }
}
},
variables: ['NEXTAUTH_URL'],
debug: true
});
// Then start Next.js
const app = next({ /* ... */ });Next.js Config Wrapper
// next.config.js
const { withRuntimeEnv } = require('next-standalone-env');
const nextConfig = {
// your config
};
module.exports = withRuntimeEnv(nextConfig, {
environments: { /* ... */ },
variables: ['NEXTAUTH_URL', 'NEXT_PUBLIC_API_URL']
});Runtime Overrides
Override any variable at runtime with the RUNTIME_ prefix:
# Override NEXTAUTH_URL regardless of APP_ENV
docker run \
-e APP_ENV=production \
-e RUNTIME_NEXTAUTH_URL=https://custom.example.com \
your-imageEnvironment Variable Priority
RUNTIME_*prefixed variables (highest priority)- Environment-specific configuration based on
APP_ENV - Existing environment variables
- Defaults
Docker Example
# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
# Copy package files
COPY package*.json ./
RUN npm ci
# Copy source
COPY . .
# Build Next.js
RUN npm run build
# Production stage
FROM node:20-alpine AS runner
WORKDIR /app
# Install next-standalone-env
RUN npm install next-standalone-env
# Copy built application
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
# Copy runtime config (optional)
COPY runtime-env.config.js ./
# Use runtime-aware server
CMD ["npx", "next-standalone-env/server"]Kubernetes Example
apiVersion: apps/v1
kind: Deployment
metadata:
name: nextjs-app
spec:
template:
spec:
containers:
- name: app
image: your-image:latest
env:
- name: APP_ENV
value: "development" # or "production", "staging"
- name: NODE_ENV
value: "production" # Always production for Next.js
# Secrets still work normally
- name: AZURE_AD_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: auth-secrets
key: client-secretHow It Works
- Build Time: Next.js builds with default/production values
- Container Start: Our server runs before Next.js
- Runtime Patch: Environment variables are updated based on
APP_ENV - Next.js Start: Next.js starts with the correct runtime values
Common Use Cases
Multi-tenant SaaS
{
environments: {
'customer-a': {
vars: {
NEXTAUTH_URL: 'https://a.example.com',
TENANT_ID: 'customer-a'
}
},
'customer-b': {
vars: {
NEXTAUTH_URL: 'https://b.example.com',
TENANT_ID: 'customer-b'
}
}
}
}Feature Flags
{
environments: {
'production': {
vars: {
FEATURE_NEW_UI: 'false'
}
},
'canary': {
vars: {
FEATURE_NEW_UI: 'true'
}
}
}
}Regional Deployments
{
environments: {
'us-east': {
vars: {
API_ENDPOINT: 'https://us-east.api.example.com',
REGION: 'us-east-1'
}
},
'eu-west': {
vars: {
API_ENDPOINT: 'https://eu-west.api.example.com',
REGION: 'eu-west-1'
}
}
}
}Debugging
Enable debug mode to see what's happening:
docker run -e DEBUG=true -e APP_ENV=development your-imageOutput:
[next-standalone-env] Starting server with environment: development
[next-standalone-env] Setting NEXTAUTH_URL=https://dev.example.com (was: https://prod.example.com)
[next-standalone-env] Server ready on http://0.0.0.0:3000
[next-standalone-env] NextAuth URL: https://dev.example.com
[next-standalone-env] Environment: developmentMigration Guide
From Multiple Dockerfiles
Before:
# Dockerfile.dev
ENV NEXTAUTH_URL=https://dev.example.com
# ... build ...
# Dockerfile.prod
ENV NEXTAUTH_URL=https://prod.example.com
# ... build ...After:
# Single Dockerfile
# No environment-specific variables!
# ... build ...
CMD ["npx", "next-standalone-env/server"]From Build Arguments
Before:
docker build --build-arg NEXTAUTH_URL=https://dev.example.com -t app:dev .
docker build --build-arg NEXTAUTH_URL=https://prod.example.com -t app:prod .After:
docker build -t app:latest .
docker run -e APP_ENV=development app:latest
docker run -e APP_ENV=production app:latestFAQ
Does this work with Vercel/Netlify?
No, this is specifically for self-hosted Next.js deployments using standalone output mode.
Does it work with all environment variables?
It works with any environment variable. However, NEXT_PUBLIC_* variables that are inlined during build may still need rebuilding for changes.
Is there a performance impact?
Minimal. Environment resolution happens once at server startup, not per request.
Can I use this with PM2/cluster mode?
Yes, the environment is patched before Next.js starts, so it works with any process manager.
Contributing
Contributions welcome! Please feel free to submit a Pull Request.
License
MIT © Vinnie Espo
Related Projects
- next-bun-docker - Webpack fixes for Bun + Docker + Next.js
Support
If this package helps you, please consider:
- ⭐ Starring the repo
- 🐛 Reporting issues
- 🔀 Contributing improvements
- 📢 Sharing with others who might need it
