Deploying a Next.js App with NEXT_PUBLIC Environment Variables with Docker using Fly.io

Carbonable.io
4 min readOct 11, 2024

--

In the world of modern web development, Next.js has become a go-to framework for building robust and scalable applications. When deploying these applications, especially to platforms like Fly.io using Docker, managing environment variables — particularly those prefixed with `NEXT_PUBLIC_` — can be challenging. This guide will walk you through the process of setting up your Next.js app for deployment to Fly.io, with a special focus on handling `NEXT_PUBLIC_` environment variables correctly in a dockerized environment.

Why is this important?

`NEXT_PUBLIC_` variables in Next.js are unique because they need to be embedded at build time, not runtime. This presents a challenge in dockerized environments where we typically set environment variables at runtime. Our solution involves passing these variables as build arguments, ensuring they’re available during the build process and in the final application.

Prerequisites

Before we begin, make sure you have the following:

1. A Next.js application

2. Docker installed on your local machine

3. A Fly.io account and the `flyctl` CLI installed

Step 1: Preparing Your Dockerfile

First, let’s set up our Dockerfile to handle the `NEXT_PUBLIC_` environment variables correctly. Create a `Dockerfile` in your project root with the following content:

DockerFile

# Build stage
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .

# Add these lines for each NEXT_PUBLIC variable
ARG NEXT_PUBLIC_EXAMPLE_VAR
ENV NEXT_PUBLIC_EXAMPLE_VAR=$NEXT_PUBLIC_EXAMPLE_VAR
ARG NEXT_PUBLIC_ANOTHER_VAR
ENV NEXT_PUBLIC_ANOTHER_VAR=$NEXT_PUBLIC_ANOTHER_VAR
# Add any other NEXT_PUBLIC variables your app needs

RUN npm run build

# Production stage
FROM node:18-alpine AS runner
WORKDIR /app
ENV NODE_ENV production

COPY --from=builder /app/next.config.js ./
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json

EXPOSE 3000
CMD ["npm", "start"]

This Dockerfile sets up a multi-stage build process, accepting `NEXT_PUBLIC_` variables as build arguments and setting them as environment variables.

Step 2: Configuring Fly.io

Next, let’s set up our Fly.io configuration. Create a `fly.toml` file in your project root:

fly.toml

app = "your-app-name"

[build]
[build.args]
NEXT_PUBLIC_EXAMPLE_VAR = ""
NEXT_PUBLIC_ANOTHER_VAR = ""
# Add any other NEXT_PUBLIC variables your app needs

[env]
PORT = "8080"

[http_service]
internal_port = 3000
force_https = true
auto_stop_machines = true
auto_start_machines = true

Leave the values for the `NEXT_PUBLIC_` variables empty in `fly.toml`. We’ll provide the actual values during deployment.

Step 3: Creating a Deployment Script

To make our deployment process smooth and secure, let’s create a deployment script that reads from a `.env` file and passes our environment variables as build args. Create a file named `deploy.sh` in your project root:

deploy.sh

#!/bin/bash

# Function to print debug information
debug_info() {
echo "Debug Info:"
echo "CONFIG_FILE: $CONFIG_FILE"
echo "BUILD_ARGS: $BUILD_ARGS"
echo "NEXT_PUBLIC variables:"
env | grep NEXT_PUBLIC_
}

# Initialize variables
CONFIG_FILE=""

# Check if the script is being sourced
if [[ "${BASH_SOURCE[0]}" != "${0}" ]]; then
echo "Script is being sourced. Please run it as a separate command."
return 1
fi

# Parse command line arguments
while getopts ":c:" opt; do
case $opt in
c)
CONFIG_FILE="-c $OPTARG"
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
:)
echo "Option -$OPTARG requires an argument." >&2
exit 1
;;
esac
done

# Read .env file
if [[ -f .env.deploy ]]; then
set -a
source .env.deploy
set +a
else
echo ".env.deploy file not found!"
exit 1
fi

# Construct build args string
BUILD_ARGS=""
for var in "${!NEXT_PUBLIC_@}"; do
BUILD_ARGS="$BUILD_ARGS --build-arg $var=${!var}"
done

# Print debug information
debug_info

# Deploy with build args and optional config file
echo "Deploying with command: fly deploy $CONFIG_FILE $BUILD_ARGS"
fly deploy $CONFIG_FILE $BUILD_ARGS

Make this script executable by running:

chmod +x deploy.sh

Step 4: Setting Up Your .env File

Create a `.env` file in your project root with your actual environment variable values:

NEXT_PUBLIC_EXAMPLE_VAR=your_value_here
NEXT_PUBLIC_ANOTHER_VAR=another_value_here
# Add other NEXT_PUBLIC variables as needed

Make sure to add this `.env` file to your `.gitignore` to keep your sensitive values secure.

Step 5: Deployment

Now you’re ready to deploy! Run the deployment script:

./deploy.sh

If you need to use a custom configuration file, you can pass it with the `-c` flag:

./deploy.sh -c path/to/your/custom-config.toml

How It All Works Together

1. The Dockerfile sets up build arguments for each `NEXT_PUBLIC_` variable.

2. The `fly.toml` file declares these variables (with empty values) so Fly.io knows to expect them.

3. The `deploy.sh` script reads the actual values from your `.env` file and passes them as build arguments to `fly deploy`.

4. During the Docker build process on Fly.io, these values are used to set environment variables, which are then available during the Next.js build process.

5. The resulting Docker image has the `NEXT_PUBLIC_` values baked in, ready for use in your application.

Conclusion

By following these steps, you’ve set up a robust deployment process for your Next.js application on Fly.io, ensuring that your `NEXT_PUBLIC_` environment variables are correctly handled during the build process and available to your application at runtime.

This approach solves the challenge of using build-time environment variables in a dockerized environment, making it easier to deploy Next.js applications with configuration that needs to be available on the client-side.

Remember to keep your `.env` file updated with the correct values and never commit it to version control. With this setup, you can easily manage different environments and keep your deployment process secure and manageable.

Happy coding and deploying!

--

--

Carbonable.io
Carbonable.io

Written by Carbonable.io

Nature-based Solutions Portfolio Management reinvented. 🌎 Source, Fund, Drive, and Monitor Provable Ecological Restoration. ✅ Powered by Blockchain Technology