惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

C
Check Point Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
C
CERT Recently Published Vulnerability Notes
Apple Machine Learning Research
Apple Machine Learning Research
酷 壳 – CoolShell
酷 壳 – CoolShell
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
J
Java Code Geeks
Jina AI
Jina AI
雷峰网
雷峰网
M
MIT News - Artificial intelligence
小众软件
小众软件
H
Help Net Security
The Register - Security
The Register - Security
T
Tailwind CSS Blog
D
DataBreaches.Net
大猫的无限游戏
大猫的无限游戏
有赞技术团队
有赞技术团队
G
Google Developers Blog
云风的 BLOG
云风的 BLOG
Google DeepMind News
Google DeepMind News
月光博客
月光博客
Project Zero
Project Zero
P
Proofpoint News Feed
S
Security @ Cisco Blogs
L
LINUX DO - 最新话题
I
InfoQ
Vercel News
Vercel News
V
Vulnerabilities – Threatpost
S
Schneier on Security
Spread Privacy
Spread Privacy
Hugging Face - Blog
Hugging Face - Blog
D
Docker
博客园 - 【当耐特】
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
T
The Blog of Author Tim Ferriss
博客园 - 聂微东
宝玉的分享
宝玉的分享
Recorded Future
Recorded Future
K
Kaspersky official blog
L
LINUX DO - 热门话题
Stack Overflow Blog
Stack Overflow Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
腾讯CDC
A
About on SuperTechFans
D
Darknet – Hacking Tools, Hacker News & Cyber Security
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
GbyAI
GbyAI
Schneier on Security
Schneier on Security
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
B
Blog RSS Feed

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Building a Multi-Environment Web App on App Platform
Peace Sandy · 2026-04-29 · via DEV Community

Deploying straight to production is one of the most common mistakes developers make early in their careers. One environment, one URL, and one quiet hope that nothing will break. It works until it doesn't.

A single bad push on a Friday afternoon can leave a client's homepage blank, with no staging environment to catch the issue and no safe place to roll back. Deploying without a safety net isn't a workflow; it's a risk.

Many developers stay in this pattern longer than they should. A single live environment feels faster to maintain. But as applications scale, the approach becomes fragile. Bugs reach users before they are caught.

Multi-Environment architecture solves this. By separating your application into distinct staging and production deployments, you create a safe layer where changes are validated before they affect real users.

Each environment runs the same codebase with different configurations, so what you test in staging is exactly what ships to production, no surprises.

In this tutorial, you will build a Next.js application and deploy it across two fully independent environments on DigitalOcean App Platform, a managed cloud service that builds and deploys apps directly from a GitHub repository.

You will configure environment variables to control how the app behaves in each environment, set up a Git branch strategy that maps staging and production to separate deployments, and enable autodeploy so that every push automatically triggers a rebuild.

By the end, you will have a working multi-environment setup and a repeatable promotion workflow that lets you ship with confidence.

Prerequisites

To complete this tutorial, you will need:

  • Node.js v18 or higher is installed on your machine, including npm, which comes bundled with it. You can check your versions by running node -v and npm -v in your terminal. If you need to install Node.js, follow How To Install Node.js and Create a Local Development Environment for your system.

  • Git is installed on your local machine. Run git --versionto check. If you need to install it, follow How To Install Git and set it up on your machine before continuing.

  • A GitHub account with a new empty repository named multi-env-app. App Platform deploys directly from GitHub, so your code must be hosted there before you can connect it to DigitalOcean. You can sign up for free at github.

  • A DigitalOcean account with access to App Platform. If you are new to DigitalOcean, you can sign up at DigitalOcean and follow the App Platform getting started documentation before proceeding.

  • Familiarity with Next.js and React, specifically what components and pages are and how they relate to each other. If you need to build your foundational knowledge first, follow How To Create a Next.js App before starting this tutorial.

Key Takeaways:

  • A multi-environment setup separates your application into distinct deployments, typically staging and production, allowing you to test changes safely before they affect real users.

  • DigitalOcean App Platform deploys directly from a GitHub repository, automatically rebuilding and redeploying your app every time you push to a connected branch.

  • Environment variables in Next.js prefixed with NEXT_PUBLIC_ are embedded into the client bundle at build time. They are publicly visible, while variables without the prefix remain server-side only and should be used for secrets.

  • Each App Platform deployment maintains its own set of environment variables, meaning your staging and production apps can point to different APIs and configurations while running identical code.

  • A Git branch-based promotion workflow where the staging branch maps to the staging environment and the main branch maps to production gives you a reliable, repeatable process for shipping changes safely.

  • Keeping .env.local out of version control by adding it to .gitignore and committing only .env.example as a reference template is a critical security practice that prevents secrets from being accidentally exposed on GitHub.

What is Multi-Environment?

A multi-environment setup means running separate, isolated versions of the same application, each with its own configuration, URL, and purpose.

Rather than maintaining one live deployment that everyone shares, you maintain distinct environments that represent different stages of your development workflow.

Most teams work with three environments:

  • Local is where development happens. It runs on your machine, points to local APIs, and is never visible to anyone else. You can break things freely here without consequence.

  • Staging is a production-like environment used for testing and review. It runs the same code and infrastructure as production but uses test data and non-critical API endpoints. Think of it as a dress rehearsal before the real show.

  • Production is the live environment where real users interact. Changes here have real consequences, which is why nothing should reach it without first passing through staging.

What makes this work is that each environment runs the same codebase with different configuration values. There is no separate "staging version" of your code.

The same application reads its environment variables at build time and behaves accordingly, which is exactly what you will see in action throughout this tutorial.

The cost of skipping this setup only becomes clear after something breaks in production. A multi-environment architecture does not prevent bugs, but it ensures they are caught early, when the stakes are low, before they reach the people who depend on your product.

Building a Simple Next.js App

In this section, we are going to build a simple Next.js App

Step 1: Creating the Repository on GitHub

In this step, you will create a GitHub repository to host your project. App Platform deploys directly from GitHub, so your code must be hosted there before you can connect it to DigitalOcean.

Navigate to github and click the "New" button to create a new repository. Fill in the details as follows:

  • Set the Repository name to multi-env-app
  • Set the Visibility to Public
  • Check the box labelled Add a README file

Checking the Add a README file option initializes the repository with a first commit. This means Git treats the repository as having an existing history, so you can clone it to your local machine immediately without running any additional initialization commands.

Click Create repository when you are done. You have created your remote repository on GitHub, and it is ready to receive your project files. In the next step, you will clone it to your local machine and open it in your code editor.

Step 2: Cloning the Repository and Opening It in VS Code

In this step, you will clone the repository to your local machine and open it in VS Code so you have a working local copy ready for development.

Run the following command in your terminal to clone the repository, replacing YOUR_USERNAME with your actual GitHub username:

git clone https://github.com/YOUR_USERNAME/multi-env-app.git

Enter fullscreen mode Exit fullscreen mode

This command downloads the repository from GitHub and creates a local copy of it in a folder named multi-env-app on your machine.

Move into the project directory:

cd multi-env-app

Enter fullscreen mode Exit fullscreen mode

Then open the folder in VS Code:

code.

Enter fullscreen mode Exit fullscreen mode

The code. command launches VS Code with the current directory open as the project workspace. If this command is not recognized in your terminal, follow the VS Code Shell Command installation guide to enable it.

At this point, your project contains only the README.md file that GitHub created. In the next step, you will scaffold the Next.js application inside this directory.

Step 3: Scaffolding the Next.js Application

In this step, you will scaffold a new Next.js application directly into your existing project directory. This generates all the files and folders you need to start building your app.

Run the following command to create the Next.js application inside the current directory:

npx create-next-app@latest .

Enter fullscreen mode Exit fullscreen mode

The dot (.) tells the scaffolding tool to use the current directory instead of creating a new nested folder.

Without it, the tool would create a multi-env-app folder inside your existing multi-env-app folder, which would break your repository structure.

The tool will prompt you with a series of configuration questions. Answer them exactly as follows:

Would you like to use TypeScript?             No
Would you like to use ESLint?                 Yes
Would you like to use Tailwind CSS?           Yes
Would you like your code inside a src/ dir?   No
Would you like to use App Router?             Yes
Would you like to use Turbopack?              No
Would you like to customize the import alias? No

Enter fullscreen mode Exit fullscreen mode

Here is why each choice matters for this tutorial:

  • TypeScript: No — This tutorial uses JavaScript to keep the code accessible to a wider audience. The concepts apply equally to TypeScript projects.

  • ESLint: Yes — ESLint catches common code errors during development and is a standard part of most production Next.js setups.

  • Tailwind CSS: Yes — You will use Tailwind utility classes to style the environment banner directly in your JSX, without creating any separate CSS files.

  • App Router: Yes — The App Router is the current recommended routing system in Next.js and is built around this tutorial.

  • src/ directory, Turbopack, import alias: No—These are not needed for this tutorial and would add unnecessary complexity.

Once the prompts are complete, create-next-app will install all dependencies and automatically generate your project files.

You now have a fully scaffolded Next.js application in your project directory. In the next step, you will create the environment variable files that allow your app to behave differently depending on where it is deployed.

Step 4: Creating the Environment Variable Files

In this step, you will create environment variable files that allow your app to read different configuration values depending on where it runs. You will also update your .gitignore file to ensure that secrets are never committed to GitHub.

Open your editor and create a new file named .env.local in the root of your project. Add the following content:

NEXT_PUBLIC_APP_ENV=local
NEXT_PUBLIC_API_URL=http://localhost:3000/api

Enter fullscreen mode Exit fullscreen mode

This file holds the configuration values for your local development environment. Next.js loads .env.localautomatically when you run the development server, so these values will be available to your app without any additional setup.

Next, create a second file named .env.example in the same root location and add the following:

NEXT_PUBLIC_APP_ENV=
NEXT_PUBLIC_API_URL=

Enter fullscreen mode Exit fullscreen mode

Unlike .env.local, this file contains the variable names but no values. It acts as a reference template for anyone who clones the repository, showing them exactly which variables they need to configure without exposing any real values.

Now open the .gitignorefile that Next.js generated automatically and locate the following line:
.env *

This wildcard pattern tells Git to ignore all files that begin with .env, which correctly prevents .env.local from being committed to GitHub. However, it also ignores .env.example, which you do want to commit so other developers can reference it. Add the following line directly below it:

.env*
!.env.example

Enter fullscreen mode Exit fullscreen mode

The exclamation mark creates an exception to the wildcard rule, telling Git to track .env.example, even though all other .env files are ignored. With this change in place, your local secrets stay protected while the reference template remains visible in the repository.

You have set up your environment variable files and secured your .gitignore. In the next step, you will build the environment banner component that reads these values and displays them visually in the browser.

Step 5: Building the Environment Banner Component

In this step, you will create a React component that reads your environment variables and displays them as a colored banner at the top of the page. This makes it immediately clear which environment you are looking at whenever you open the app in a browser.

Create a new folder named components in the root of your project. Inside it, create a new file named EnvBanner.js:

mkdir components && touch components/EnvBanner.js

Enter fullscreen mode Exit fullscreen mode

Open components/EnvBanner.js in your editor and add the following code:

jsx
// components/EnvBanner.js
export default function EnvBanner() {
  const env = process.env.NEXT_PUBLIC_APP_ENV;
  const apiUrl = process.env.NEXT_PUBLIC_API_URL;

  const styles = {
    local:      { bg: "bg-gray-800",  label: "LOCAL" },
    staging:    { bg: "bg-yellow-500", label: "STAGING" },
    production: { bg: "bg-green-600", label: "PRODUCTION" },
  };

const current = styles[env] || { bg: "bg-gray-500", label: env || "UNKNOWN" };

  return (
    <div className={`${current.bg} text-white text-sm font-mono px-4 py-2 text-center`}>
      🌍 Environment: <strong>{current.label}</strong> &nbsp;|&nbsp; API:{" "}
      <code className="bg-black/20 px-1 rounded">{apiUrl}</code>
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

Here is what each part of the component does:

  • process.env.NEXT_PUBLIC_APP_ENV and process.env.NEXT_PUBLIC_API_URL These lines read the two environment variables you defined in .env.local. Next.js makes any variable prefixed with NEXT_PUBLIC_available directly in the browser bundle.

  • styles object — This maps each environment name to a Tailwind background color and a display label. local renders in dark gray, staging in yellow, and production in green, making each environment visually distinct at a glance.

  • current fallback — If NEXT_PUBLIC_APP_ENV is not set or holds an unexpected value, the banner falls back to gray and displays whatever value it finds. This makes misconfigurations immediately visible in the browser rather than failing silently.

  • The returned JSX — Renders a full-width banner using the resolved color and label, with the API URL displayed alongside it for quick reference.

You have built the banner component that will visually reflect your environment configuration. In the next step, you will update the homepage to render this component so it appears at the top of every page.

Step 6: Updating the Homepage

In this step, you will update the homepage to render the environment banner and display a config card showing the current environment variable values. This gives you a single page that makes your entire environment configuration visible at a glance.

Open app/page.js in your editor and replace its entire contents with the following:

jsx
// app/page.js
import EnvBanner from "@/components/EnvBanner";

export default function Home() {
  return (
    <main className="min-h-screen bg-gray-50">
      <EnvBanner />

      <div className="max-w-2xl mx-auto mt-20 px-6 text-center">
        <h1 className="text-4xl font-bold text-gray-800 mb-4">
          Multi-Environment App
        </h1>
        <p className="text-gray-500 text-lg">
          This app reads its configuration from environment variables.
          The banner above changes depending on where this app is deployed.
        </p>

        <div className="mt-10 bg-white rounded-xl shadow p-6 text-left">
          <h2 className="text-sm font-semibold text-gray-400 uppercase mb-3">
            Current Config
          </h2>
          <ul className="space-y-2 font-mono text-sm">
            <li>
              <span className="text-gray-400">APP_ENV: </span>
              <span className="text-indigo-600 font-bold">
                {process.env.NEXT_PUBLIC_APP_ENV || "not set"}
              </span>
            </li>
            <li>
              <span className="text-gray-400">API_URL: </span>
              <span className="text-indigo-600">
                {process.env.NEXT_PUBLIC_API_URL || "not set"}
</span>
            </li>
          </ul>
        </div>
      </div>
    </main>
  );
}


Enter fullscreen mode Exit fullscreen mode

Here is what each part of the updated homepage does:

  • import EnvBanner from@/components/EnvBanner — This imports the banner component you built in the previous step. The @/prefix is an alias for the project root, configured automatically by create-next-app. It lets you import files without writing long relative paths like ../../components/EnvBanner.

  • EnvBanner — Renders the colored environment banner at the very top of the page, before any other content.

  • The config card — Displays the current values of NEXT_PUBLIC_APP_ENV and NEXT_PUBLIC_API_URL directly on the page. If either variable is not set, it falls back to displaying "not set," so misconfigurations are immediately visible.

You have updated the homepage to display both the environment banner and the current configuration values. In the next step, you will run the app locally to confirm everything is working before deploying to App Platform.

Step 7: Running the App Locally

In this step, start the development server and verify that your app is reading environment variables correctly before deploying it to App Platform.

Run the following command to start the development server:

npm run dev

Enter fullscreen mode Exit fullscreen mode

This command compiles your Next.js application and starts a local server that watches for file changes and reloads automatically.

Open your browser and navigate to http://localhost:3000. You should see the following output across the top of the page:

Output
🌍 Environment: LOCAL  |  API: http://localhost:3000/api

Enter fullscreen mode Exit fullscreen mode

The dark gray banner confirms that your app is reading NEXT_PUBLIC_APP_ENV and NEXT_PUBLIC_API_URL directly from your .env.local file. Below the banner, the config card will display the same values, showing that no values are hardcoded into the UI; everything comes from configuration.

This is an important distinction. When you deploy this same codebase to App Platform in the next steps and set NEXT_PUBLIC_APP_ENV=staging, the banner will turn yellow and update automatically without a single line of code changing.

The code remains identical across all environments; only the configuration changes.

You have confirmed that your app is environment-aware and working correctly on your local machine.

In the next step, you will commit your project to GitHub so that App Platform can access it for deployment.

Step 8: Committing and Pushing to GitHub

In this step, you will commit your project files to Git and push them to GitHub so that App Platform can access your code for deployment.

Before staging any files, confirm that .env.local is not going to be committed by running:

git status

Enter fullscreen mode Exit fullscreen mode

You should see output similar to the following:

Output
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        .env.example
        components/
        app/

Enter fullscreen mode Exit fullscreen mode

.env.example should appear as an untracked file, but .env.local should not appear anywhere in the output. If .env.local is visible, stop and review your .gitignore before continuing.

Committing it would expose your local configuration to anyone who can view the repository on GitHub.

Once you have confirmed everything looks correct, stage all the files:

git add.

Enter fullscreen mode Exit fullscreen mode

Commit the staged files with a descriptive message:

git commit -m "initial Next.js app with env banner."

Enter fullscreen mode Exit fullscreen mode

Then push the commit to GitHub:

git push origin main

Enter fullscreen mode Exit fullscreen mode

This pushes your code to the main branch of your remote repository on GitHub, which is the branch App Platform will connect to for the production environment.

After the push completes, visit your repository on GitHub and confirm that components/EnvBanner.js, app/page.js, and .env.example are all visible in the file list. The .env.localfile should not appear anywhere in the repository.

You have pushed your project to GitHub and confirmed that your secrets are protected.

In the next step, you will connect your repository to DigitalOcean App Platform and deploy your staging environment.

Creating the Staging Environment

Throughout this tutorial, you will use a simple two-branch strategy that maps directly to your two environments:

  • The main branch represents production. Code on this branch is live, and real users can see it. Nothing should reach main without first being tested on staging.

  • The staging branch represents the staging environment. This is where all new work lands first. It is a safe, production-like environment where you can verify that changes behave correctly before they affect real users.

In practice, the workflow looks like this: you build and test a new feature on the staging branch, confirm it works on the staging URL, and then merge it into main to promote it to production.

App Platform watches both branches independently and redeploys the appropriate environment whenever either one receives a push.

To create the staging branch, run the following in your terminal:

git checkout -b staging
git push origin staging

Enter fullscreen mode Exit fullscreen mode

You now have two branches on GitHub: main and staging, and your repository is ready to be connected to App Platform.

In the next step, you will create two separate App Platform apps, one pointing at each branch, and configure them with their own environment variables.

Create Your First App on App Platform

In this section, we will cover how to deploy the Next.js application you just built to DigitalOcean App Platform with separate staging and production environments.

You will connect to a GitHub repository, configure environment variables for each environment, and verify that each deployment reads the correct values.

By the end, you will have a live staging URL displaying the correct environment banner and a clear process you can repeat for production.

Step 1: Accessing App Platform

In this step, you will log in to your DigitalOcean account and navigate to App Platform, the service that hosts, builds, and deploys your Next.js application directly from GitHub.

Open your browser and go to DigitalOcean.

Enter your email and password, then click Log In. Once you are on the control panel dashboard, click App Platform in the left-hand sidebar.

This opens the App Platform section, which lists all your deployed apps. In the top-right corner of the Apps page, click the blue Create App button.

After clicking Create App, you are taken to a multi-step wizard. The first screen, titled Create App, asks you to choose a source for your code.

In the next step, you will connect your GitHub repository, so App Platform knows where to pull your code from.

DigitalOcean App Platform dashboard showing multiple deployment environments and how to create an app

Step 2: Connecting Your GitHub Repository

In this step, you will link DigitalOcean to your GitHub account and grant it access to the multi-env-app repository.

This connection is what allows App Platform to pull your code and trigger automatic deployments whenever you push a new commit.

The Create App wizard opens on the source selection screen. Under Git Provider, click the GitHub tile. A Connect to GitHub button will appear; click it.

Connect and Select Repository on the App Platform

A GitHub OAuth pop-up window will open. Do not close it. Inside the pop-up, click Authorize DigitalOcean to grant DigitalOcean read access to your GitHub repositories.

GitHub will then ask you to choose which repositories to share. Select only the repositories to limit access to just the repository this tutorial needs.

Use the search box to find multi-env-app, select it, then click Install & Authorize.

The pop-up will close automatically, and you will be returned to the DigitalOcean dashboard with your GitHub account now connected.

In the next step, you will select the branch and configure the repository source settings to specify which branch to deploy from.

Configure the repository source

Configure the repository source

With GitHub connected, you will now configure the repository source settings. Under Repository, open the dropdown and select multi-env-app. Under Branch, select staging.

You are setting up the staging environment first, so all deployments from this app will track the staging branch.

Leave Source Directory set to / since your Next.js project lives in the root of the repository.

Check the Autodeploy checkbox to have App Platform rebuild and redeploy automatically whenever you push a commit to the staging branch.

This is what makes your deployment workflow hands-free once the initial setup is complete.

Click Next to continue to the Resources screen.

Step 3: Configure Your App Resources

In this step, you will review the auto-detected configuration for your Next.js application and select a pricing plan before deploying.

When App Platform scans your repository, it identifies the project type and suggests a default build configuration. You will see one detected component with the following defaults:

Name: multi-env-app (or web)
Type: Web Service
Build Command: npm run build
Run Command: npm start

If either command is missing, click Edit on the component. Enter npm run build in the Build Command field, and npm start in the Run Command field, then click Save.

These commands tell App Platform how to compile your Next.js project and start the production server.

Next, you will select a pricing plan for your web service. Click Edit Plan on the right side of the detected Web Service component.

On the plan selection screen, click Basic to select the Basic plan, which costs $5 per month.

Under Instance Size, select the smallest available option: 512 MB RAM / 1 vCPU is sufficient for testing purposes. Click Back to return to the Resources screen.

Once you have confirmed your resource configuration, click Next to continue.You have configured your app's build settings and selected a pricing plan.

In the next step, you will set up your environment variables so that App Platform can pass the correct configuration values to your application at build time and runtime.

Step 4: Setting Environment Variables

In this step, you will configure the environment variables that define your application's staging environment. Because Next.js reads these values at build time and embeds them directly in the browser bundle, setting them correctly here is the most critical part of this setup.

You should now be on the Environment Variables screen. Click Edit next to your web service component name to begin adding variables.

Start by adding the first variable. Click Add Variable, and a row with two input fields and a checkbox will appear. In the Key field, enter the following:

NEXT_PUBLIC_APP_ENV

In the Value field, enter:

staging

Enter fullscreen mode Exit fullscreen mode

Leave the Encrypt checkbox unchecked. Because this variable uses the NEXT_PUBLIC_ prefix, Next.js intentionally exposes it to the browser, so encryption is not necessary here.

Next, click Add Variable again to create a second row. In the Key field, enter the following:

NEXT_PUBLIC_API_URL

Enter fullscreen mode Exit fullscreen mode

In the Value field, enter your staging API endpoint:

https://staging-api.example.com

Enter fullscreen mode Exit fullscreen mode

Leave Encrypt unchecked for this variable as well, then click Save to confirm both variables.You have configured the environment variables that distinguish your staging deployment from production.

In the next step, you will review your settings and deploy the application to App Platform.

Setting Environment Variables

Never encrypt public variables. Any key prefixed with NEXT_PUBLIC_ is embedded in the compiled JavaScript and sent to every browser that loads the page.

Checking "Encrypt" on these variables will cause the build to fail because Next.js cannot access encrypted values at build time.

Reserve encryption only for server-side secrets such as database passwords.

After saving, you should see a confirmation summary showing both variables:

NEXT_PUBLIC_APP_ENV   =  staging
NEXT_PUBLIC_API_URL   =  https://staging-api.example.com

Enter fullscreen mode Exit fullscreen mode

Click "Next" to continue

Step 5: Name Your App and Choose a Region

In this step, you will give your app an identifiable name and select the data center that will host it.

You should now be on the Info screen. Click inside the App Name field, clear the existing value, and enter the following:

multi-env-staging

Enter fullscreen mode Exit fullscreen mode

Name your project Region

Naming your app this way makes it immediately clear which environment you are looking at in the dashboard, particularly once you have both staging and production apps listed side by side.

Next, open the Region dropdown and select the data center closest to your users. For example, choose New York (NYC) on the US East Coast, Amsterdam (AMS) in Europe, or Singapore (SGP) in the Asia-Pacific. Placing your app geographically close to your users reduces latency and improves response times.

Once you have set the name and region, click Next to continue. You have named your staging app and selected a hosting region. In the next step, you will review your complete configuration before deploying the application.

Step 6: Reviewing and Creating Your App

In this step, you will review your complete configuration and deploy your staging application to App Platform.

The final screen is a summary of all the configurations you have made. Read through it carefully and confirm that each of the following values is correct before proceeding:

  • Source: multi-env-app repository, staging branch
  • Autodeploy: Enabled
  • Type: Web Service (Next.js)
  • Plan: Basic, $5/month
  • Environment Variables: NEXT_PUBLIC_APP_ENV=staging and NEXT_PUBLIC_API_URL set
  • App Name: multi-env-staging

If anything looks incorrect, click Back to return to the relevant screen and correct it before proceeding. Once you are satisfied that all values are correct, click Create Resources to deploy your application.

You have reviewed your configuration and triggered your first deployment. In the next step, you will monitor the build process and verify that your staging environment is running correctly.

Step 7: Monitoring the Deployment

In this step, you will watch the build log to confirm that your application deploys successfully through each stage of the pipeline.

After clicking Create Resources, App Platform immediately begins building and deploying your app. A live log will appear on screen as the process runs. You will see the following stages complete in order:

  • Cloning repository: App Platform pulls the code from the staging branch of the multi-env-app repository on GitHub.

  • Installing dependencies: App Platform runs npm install to install all packages listed in package.json.

  • Building: App Platform runs npm run build, which compiles the Next.js application and embeds your environment variables into the output bundle.

  • Uploading artifact: The compiled build is packaged and stored in preparation for deployment.

  • Deploying: The container starts, and your app begins serving traffic.

If any stage fails, App Platform will log the error. Read the error message carefully; most build failures at this stage are caused by a missing environment variable or an incorrect build command.

If that happens, click Settings to review your configuration, make the necessary correction, and then trigger a new deploy by clicking Actions > Force Rebuild and Deploy.

Once all five stages are completed successfully, App Platform will display a green Live badge next to your app name. You have successfully deployed your staging environment to App Platform.

Building the project

A successful deployment produces output similar to the following at the end of the build log:

The initial deployment typically takes 3 to 5 minutes. The app's status indicator changes from "Building" (yellow) to "Deployed" (green) when it is ready.

If the deployment fails, click on the failed deployment to expand the full build log. Scroll to the first line marked ERROR to find the cause. Common causes are a missing dependency in package.json, a TypeScript type error that fails the build, or an incorrectly named environment variable.

Step 8: Test Your Staging Deployment

In this step, you will open your deployed application in a browser and confirm that it is reading the correct environment variables.

Navigate to the Deployments tab in App Platform and copy your live URL, which will look like the following:

https://multi-env-staging-xxxxx.ondigitalocean.app

Live Site Staging

Paste the URL into your browser and press Enter. Once the page loads, look at the top of the screen and confirm the following:

  • A yellow banner reading Environment: STAGING is visible at the top of the page.

  • The API URL displayed on the page matches https://staging-api.example.com.

If the banner is missing or shows a different environment, your NEXT_PUBLIC_APP_ENVvariable was not set correctly. If the API URL is wrong or absent, check the value you entered for NEXT_PUBLIC_API_URL.

In either case, return to Settings > App-Level Environment Variables, correct the value, and click Actions > Force Rebuild and Deploy to redeploy with the updated configuration.

Yellow Banner Staging

Deployment confirmed. If you see the yellow STAGING banner with the correct API URL, your staging environment is live, and your environment variables are being read correctly by the Next.js application.

In the next step, you will set up your production app using the same process with a separate set of values.

Creating the Production Environment

With staging live and verified, you are ready to deploy production. The setup follows the same wizard you used for staging. This section only covers what is different.

Step 1: Create a New App and Connect the Main Branch

From the App Platform dashboard, click "Create App".

Since you already authorized DigitalOcean to access your GitHub account during the staging setup, you will not need to go through the OAuth flow again.

Under "Service Provider", select GitHub and configure the repository source as follows:

  • Repository: multi-env-app

  • Branch: main

  • Autodeploy: Enabled

Leave "Source Directory" set to / and click "Next".

Repository

Step 2: Set Production Environment Variables

On the "Environment Variables" screen, add both variables exactly as shown:

NEXT_PUBLIC_APP_ENV   =  production
NEXT_PUBLIC_API_URL   =  https://api.example.com

Enter fullscreen mode Exit fullscreen mode

Unlike the staging setup,NEXT_PUBLIC_API_URL here points to your real production API endpoint. Leave "Encrypt" unchecked for both variables for the same reasons covered in the staging setup. Click "Save", then "Next".

Step 3: Name Your App and Review

On the "Info" screen, name the app "multi-env-production" and select the same region you used for staging to keep latency consistent across environments.

On the final review screen, confirm the following before clicking "Create Resources":

  • Source: multi-env-app repository, main branch

  • Autodeploy: Enabled

  • Environment variables: NEXT_PUBLIC_APP_ENV=production and NEXT_PUBLIC_API_URL set

  • App name: multi-env-production

Name Production

Step 4: Verify the Production Deployment

Once the status shows "Deployed", copy the live URL from the Deployments tab and open it in your browser. Confirm the following:

Enivironment Building

  • A green banner reading "Environment: PRODUCTION" is visible at the top of the page
  • The API URL displayed matches https://api.example.com

If you see the green banner with the correct API URL, your production environment is live. You now have two fully independent deployments running identical code, each reading its own configuration.

productionready

Frequently Asked Questions

  • Can I use this same multi-environment setup with an existing Next.js project, or does it only work with a freshly scaffolded app?

Yes, you can apply this setup to any existing Next.js project. The core requirements are that your project has a package.json with build and start scripts, and that your environment variables follow the NEXT_PUBLIC_ prefix convention.

Simply create your .env.localand .env.example files, update your .gitignore, and connect your existing repository to App Platform following the same steps covered in this tutorial.

  • What happens if I accidentally commit .env.local to GitHub?

Remove it immediately. Run git rm --cached .env.local to stop tracking the file without deleting it locally, commit that change, and push it to GitHub.

Then, rotate any exposed values treat them as compromised regardless of whether your repository is public or private. After that, verify your .gitignore contains the .env* pattern with the !.env.example exception before continuing.

  • Will the autodeploy trigger for both staging and production every time I push to any branch?

No. Each App Platform app watches only the branch you connected it to. Pushing to staging triggers a rebuild of your staging app only, and pushing to main triggers a rebuild of production only. The two deployments are fully independent and do not affect each other.

  • Can I add more than two environments, such as a QA or preview environment?

Yes. You can create as many App Platform apps as you need, each connected to a different branch with its own environment variables. Simply create a new branch in your repository, spin up a new App Platform app pointed at that branch, and configure the appropriate variable values. The same workflow applies regardless of how many environments you add.

  • Does this setup work with frameworks other than Next.js?

Yes. App Platform supports a wide range of frameworks, including Remix, Astro, SvelteKit, and plain Node.js applications.

The environment variable strategy described in this tutorial, using platform-level variables to configure deployments without changing code, applies to any framework that reads from process.env at build or runtime.

Check the App Platform documentation for framework-specific build and run command requirements.

Conclusion

You have built and deployed a multi-environment Next.js application on DigitalOcean App Platform. You scaffolded a Next.js project, configured environment variables to control application behavior, and built an environment banner that makes your active configuration immediately visible in the browser.

You then connected your GitHub repository to App Platform and deployed two fully independent environments, staging tracking the staging branch and production tracking main, each reading its own set of environment variables from the same codebase.

You now have a repeatable promotion workflow: build and test on the staging branch, verify the changes on your staging URL, and merge into main to ship to production. Autodeploy handles the rest automatically on every push.

From here, you can extend this setup in several directions. As your application grows, you can add additional NEXT_PUBLIC_variables for feature flags, analytics keys, or third-party service endpoints without touching your application code.

For sensitive server-side values like database passwords or private API keys, revisit your App Platform environment variable settings and use the Encrypt option. These values are never embedded in the browser bundle and are safe to protect this way.

If you want to go further, DigitalOcean's App Platform documentation covers custom domains, alerting, and autoscaling for when your application is ready to grow beyond a basic plan.

You can also explore Next.js's environment variable documentation for a deeper understanding of how build-time and runtime variables differ and when to use each.

Resources

For more information about App Platform and web app deployment, you can check out the following tutorials:

This article is part of the DigitalOcean Ripple Writers program. I received compensation and platform credits for writing this content, but all technical assessments, code, and opinions are my own based on hands-on testing.