When deploying a Next.js application in a Docker container, managing environment variables can be a real challenge β especially when it comes to client-side variables prefixed with NEXT_PUBLIC_
. This is crucial for services like Google reCAPTCHA and analytics tools like PostHog, which require these variables at runtime.
The Issue: NEXT_PUBLIC_
Environment Variables and Docker
Imagine you've successfully built your Next.js app locally, only to run into a wall when you try to deploy it in a Docker container. The problem arises because NEXT_PUBLIC_
environment variables must be defined at build time. When you run the next build
command, Next.js inlines these values into the generated JavaScript, making them inaccessible at runtime.
In a typical setup where the same Docker image is used across multiple environments (dev, staging, production), this means you would need to create separate images for each environment just to accommodate different values for NEXT_PUBLIC_
. This approach leads to:
- Increased complexity in your CI/CD pipeline.
- Longer deployment times.
- A higher chance of introducing errors with each build.
For example, I encountered significant challenges when integrating PostHog for analytics, as I needed to provide the NEXT_PUBLIC_POSTHOG_KEY
in different environments without rebuilding the Docker image. Similarly, handling Google reCAPTCHA keys posed a similar issue.
The Solution: Dynamic Environment Variables with next-runtime-env
Fortunately, there's a way to avoid these headaches: using the next-runtime-env
package. This package allows you to inject environment variables into your Next.js application at runtime, enabling you to build your Docker image once and deploy it across different environments without needing to rebuild.
How to Get Started
-
Install
next-runtime-env
: Add the package to your project:
npm install next-runtime-env
-
Update Your Layout: In your
app/layout.tsx
, include the PublicEnvScript component to expose yourNEXT_PUBLIC_
variables:
// app/layout.tsx
import { PublicEnvScript } from 'next-runtime-env';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
<PublicEnvScript />
</head>
<body>
{children}
</body>
</html>
);
}
- Access Your Variables: Now you can access your environment variables within your components:
// app/client-page.tsx
'use client';
import { env } from 'next-runtime-env';
import posthog from 'posthog-js';
import { PostHogProvider } from 'posthog-js/react';
import React from 'react';
if (typeof window !== "undefined") {
posthog.init(env("NEXT_PUBLIC_POSTHOG_KEY") || "", {
api_host: "/ingest",
ui_host: "https://us.posthog.com",
person_profiles: "always",
});
}
export function CSPostHogProvider({ children }: { children: React.ReactNode }) {
return <PostHogProvider client={posthog}>{children}</PostHogProvider>;
}
Benefits of This Approach
One Image, Many Environments: With
next-runtime-env
, you can use a single Docker image across different environments, significantly simplifying your deployment strategy.Faster Deployments: Since you're not rebuilding images, your deployments are quicker and less prone to errors.
Easier Maintenance: You'll spend less time managing multiple builds, allowing you to focus on developing new features.
Conclusion
If you've ever faced the challenge of managing NEXT_PUBLIC_
,environment variables in a Dockerized Next.js application, you know how frustrating it can be. By leveraging next-runtime-env
, you can manage these variables dynamically at runtime, ensuring a smooth and efficient deployment process.
This solution keeps your Docker strategy simple, maintainable, and effective across all your environments β whether you're handling analytics tools like PostHog or integrating Google reCAPTCHA.
May your code be bug-free!
Top comments (1)
This is gold! Thank for sharing!