Guide to Deploying Next.js on DigitalOcean App Platform 2026
คู่มือ deploy Next.js บน App Platform เจาะลึก build/run command, environment variables, custom domain, ราคาแต่ละแผน พร้อมข้อควรระวัง ISR และ error ที่พบบ่อย
DigitalOcean's App Platform is a convenient way to deploy Next.js applications without managing servers yourself. However, Next.js has multiple rendering modes — static, SSR, and ISR — each requiring different build/run command configurations and component types on App Platform. This guide dives deep into Next.js deployment specifics: from build commands and environment variables to pricing tiers and common errors with their solutions.
Contents
- What is App Platform and How Does It Differ From Droplets?
- Connecting a GitHub Repository to App Platform
- Configuring Build and Run Commands for Next.js
- Environment Variables and Custom Domains
- Pricing: Static Sites Free / Web Services Starting at $5/month
- When Should You Use This Feature (Real Use Cases)
- Common Errors and How to Fix Them
- Best Practices
- FAQ
What is App Platform and How Does It Differ From Droplets?
App Platform is DigitalOcean's Platform-as-a-Service (PaaS) offering designed to deploy applications from source code without managing servers yourself. It differs fundamentally from Droplets, which are Infrastructure-as-a-Service (IaaS) — users must create a virtual machine, install Node.js, configure a process manager like PM2, set up Nginx as a reverse proxy, and request SSL certificates manually. For Next.js specifically, this distinction matters significantly because Next.js supports multiple rendering modes: static site generation (SSG), server-side rendering (SSR), and incremental static regeneration (ISR) — each requiring different runtime environments. When you push a repository with a package.json specifying the "next" dependency to GitHub and connect it to App Platform, the system automatically detects the framework via Cloud Native Buildpacks and suggests an appropriate component type. If your app contains only static pages (no API routes or SSR), you can deploy it as a "Static Site" component, which qualifies for the free tier. However, if your app has API routes, getServerSideProps, or ISR with on-demand revalidation, you must use a "Service" (Web Service) component that keeps a Node.js process running continuously to handle requests — this component type is always paid; there is no free tier. App Platform's advantages include no need to patch the OS yourself, no firewall configuration required, because DigitalOcean handles TLS termination, load balancing, and automatic restarts when processes crash. In exchange, you have less flexibility than Droplets — you cannot SSH in to tweak OS-level configs or install custom Nginx modules. For teams wanting to deploy Next.js quickly, focus on code over infrastructure, and don't have extremely high traffic requiring deep stack tuning, App Platform is clearly the better choice than Droplets.
- App Platform = PaaS (managed), Droplet = IaaS (self-managed)
- Static Site component supports SSG/static export only and qualifies for free tier
- Web Service component required for SSR/API routes/ISR — always paid
Connecting a GitHub Repository to App Platform
Based on real-world use, to connect a Next.js project to App Platform, start at cloud.digitalocean.com/apps and click Create App. Select GitHub as the source (GitLab and Docker Hub are also supported, but GitHub is the most common workflow for Next.js). If this is your first time connecting, the system will guide you to install the DigitalOcean GitHub App, which requires you to authorize repository access — you can choose either "All repositories" or specify individual repos for security. After authorization, select the repository and branch to deploy (typically main or production). If your project is a monorepo with the Next.js app in a subfolder like apps/web, you must specify the Source Directory to match that path. Otherwise, the buildpack won't find package.json at the root and the build will fail. Next, App Platform scans the repository to detect the framework. When it finds "next" in package.json dependencies, it automatically suggests a component type and default build/run commands. You can review and modify these before actually deploying. One important option is Autodeploy — when enabled, every time you push a new commit to your chosen branch, App Platform automatically triggers a build and deployment. This works like a built-in CI/CD pipeline without needing to write GitHub Actions separately — perfect for small to medium teams that don't want to maintain a separate CI/CD system. Teams needing more control over deploy steps, such as running a test suite first, can disable autodeploy and use doctl apps create-deployment to trigger deployments from an external workflow instead. Once the repository is connected successfully, App Platform displays build logs in real-time, making it easy to debug dependency or build script errors without waiting for a complete deploy.
- Create an app from cloud.digitalocean.com/apps → Create App → GitHub
- Install the DigitalOcean GitHub App to authorize repository access
- Monorepos must specify the Source Directory to match package.json location
- Enable Autodeploy to automatically build/deploy on every push
- View real-time build logs to debug issues immediately
Configuring Build and Run Commands for Next.js
Setting the correct Build and Run Commands is critical for Next.js to work properly on App Platform, because the buildpack's default guess may not match your project structure.
The standard Build Command is npm install && npm run build, though the buildpack actually runs install automatically, so npm run build (which calls next build internally) is sufficient. If your project uses yarn or pnpm, specify the matching command, such as pnpm install && pnpm build, because the buildpack selects the package manager based on the lockfile it finds (package-lock.json, yarn.lock, pnpm-lock.yaml).
The Run Command for a Web Service component is typically npm start, which maps to next start in package.json — a common mistake is forgetting that App Platform assigns the port dynamically via the $PORT environment variable, not always 3000. You must update the script to next start -p $PORT, or health checks will fail because the container isn't listening on the expected port.
For projects seeking a smaller image size and faster cold starts, enable output: 'standalone' in next.config.js, which bundles only necessary files into .next/standalone, changing the run command to node .next/standalone/server.js (you must manually copy public/ and .next/static in a build step since standalone output doesn't include them automatically).
Always pin your Node.js version by specifying "engines": {"node": "20.x"} in package.json to prevent the buildpack from selecting a different Node version, which could cause build behavior to diverge from testing.
For static-export-only deployments (no SSR/API routes), set output: 'export' in next.config.js, use next build as the Build Command, specify Output Directory as out, and select Static Site as the component type — this qualifies for the free tier but trades away all SSR/ISR features.
- Build command:
npm run build(callsnext build) - Run command must bind $PORT:
next start -p $PORT - Enable
output: 'standalone'to reduce build size and cold start time - Pin Node version using
enginesin package.json - Static export uses
output: 'export'+ Output Directoryout
Environment Variables and Custom Domains
Environment Variables on App Platform exist at two levels: App-level (shared across all components) and Component-level (specific to one service). Configure them either through the Dashboard in Settings > App-Level Environment Variables or via App Spec in YAML format.
For Next.js, a critical distinction is that variables prefixed with NEXT_PUBLIC_ are inlined into the JavaScript bundle on the client side during build — their values are baked into static files and visible to anyone opening DevTools. Never use this prefix for secrets or API keys. Variables without this prefix are available only server-side, such as in API routes or getServerSideProps.
Another common pitfall: changing NEXT_PUBLIC_* values and deploying won't auto-rebuild if no new commits are pushed. App Platform doesn't automatically rebuild because these values are baked into the build, not injected at runtime. You must manually trigger a rebuild via the Deploy button or CLI.
For sensitive data like database connection strings or API secrets, set the type to "Encrypted" (SECRET) rather than "Plain Text" (GENERAL) in the Dashboard's environment variables section, preventing values from appearing as plaintext in build logs or the UI.
For Custom Domains, go to Settings > Domains in your app, click Add Domain, and enter your desired domain. The system provides a CNAME or A/ALIAS record to configure at your DNS provider, pointing to your app's default domain (format: your-app-xxxxx.ondigitalocean.app). Once DNS propagates, App Platform automatically issues a TLS certificate from Let's Encrypt without manual Certbot setup — HTTPS is ready within minutes.
- Separate App-level and Component-level environment variables
NEXT_PUBLIC_prefix embeds values client-side at build — never use for secrets- Changing NEXT_PUBLIC_* requires manual rebuild — no auto-rebuild on value changes
Pricing: Static Sites Free / Web Services Starting at $5/month
Pricing is where many people get confused transitioning from static sites to Next.js with SSR because App Platform's free tier covers only Static Site components — up to 3 apps per account, 1 GiB bandwidth per app, no cost. This works for static exports without API routes or SSR. However, if your Next.js app uses getServerSideProps, API routes, middleware, or ISR with on-demand revalidation, you must deploy as a Web Service component with no free tier available — payment is always required. DigitalOcean discontinued the old Basic/Professional tier names in mid-2026, switching to direct container instance sizing instead. Current options are: Shared 1 vCPU / 512 MiB RAM / 50 GiB transfer per month = $5/month, Shared 1 vCPU / 1 GiB RAM / 100 GiB transfer = $10/month, Shared 1 vCPU / 1 GiB RAM / 150 GiB transfer = $12/month, Shared 1 vCPU / 2 GiB RAM / 200 GiB transfer = $25/month, and Shared 2 vCPU / 4 GiB RAM / 250 GiB transfer = $50/month. For small to medium Next.js apps with moderate traffic, the $5/month plan (512 MiB RAM) often suffices for prototyping or MVP, but if your app performs heavy ISR caching or memory-intensive operations in API routes like PDF generation or image processing, upgrade to 1 GiB or higher to prevent out-of-memory kills. Be aware: setting instance count above 1 for high traffic or zero-downtime deployment multiplies costs by instance count immediately. For example, 2 instances on a $10/month plan totals $20/month, not a fixed rate like a single Droplet — this is fundamentally different. All pricing is current as of July 2026; always verify the latest on DigitalOcean's pricing page before committing.
- Free tier: static sites only, maximum 3 apps, 1 GiB transfer/app
- Web Service has no free tier — always requires payment
- Starting plan: $5/month (1 vCPU/512 MiB RAM/50 GiB transfer)
When Should You Use This Feature (Real Use Cases)
App Platform suits Next.js deployment when teams prioritize shipping speed over detailed infrastructure control — such as MVPs or side projects that need production within minutes of pushing code, marketing websites blending static pages with small API routes for contact forms or newsletter signups, or small teams without dedicated DevOps engineers to manage Nginx/SSL/process managers. Another excellent use case is preview environments for pull requests. App Platform can automatically create separate preview deployments when PRs are opened, letting your team review new features on real URLs before merging without extra CI/CD setup — valuable for teams doing trunk-based development or frequent UI reviews. Conversely, App Platform isn't the best fit when you need fine-grained OS or network tuning — such as complex custom Nginx rewrite rules or managing large numbers of WebSocket connections requiring connection pool tweaks, when you have very high traffic where per-request costs matter and a Droplet with PM2 and Nginx would be cheaper at scale, or when your app relies heavily on ISR and needs shared caching across multiple instances, because App Platform containers lack shared filesystems — each instance has isolated cache, potentially showing inconsistent data to users when multiple instances handle traffic simultaneously. For teams uncertain about traffic scaling, starting with App Platform then migrating to Droplets or Kubernetes later as requirements clarify is pragmatic: low initial cost, no architecture lock-in, and easy to reassess when real numbers arrive.
- Ideal for MVPs, marketing sites, and teams without dedicated DevOps
- Supports automatic preview environments for pull requests
- Not suitable for deep OS/network-level tuning requirements
- ISR with multiple instances requires care — cache is not synchronized
Common Errors and How to Fix Them
The most frequent error when deploying Next.js on App Platform is build failure due to Node.js version mismatch, especially when using syntax or dependencies specific to newer Node versions. Fix this by pinning the version with the engines field in package.json to match your local testing environment.
The second common issue is successful deployment but the app is unreachable or health checks fail repeatedly. The root cause is usually the run command not binding to the $PORT that App Platform assigns dynamically — the container ends up listening on the wrong port. Update the start script to next start -p $PORT always, never hardcode 3000.
The third issue involves environment variables — client-side code reads undefined values when the NEXT_PUBLIC_ prefix is missing. Server variables won't reach the browser bundle regardless of configuration.
The fourth problem relates to ISR: pages showing stale content some requests and fresh content others, with requests landing on different instances that each maintain separate caches. Solutions include reducing instance count to 1 if traffic isn't high, or migrating cache to an external layer like Managed Valkey Database.
The fifth issue happens with monorepos: build timeout or unexpectedly long build duration. This occurs when Source Directory isn't specified correctly, causing the buildpack to install dependencies for the entire monorepo instead of just the Next.js app. Set Source Directory precisely and consider using workspace filtering like pnpm --filter in the build command to narrow scope.
- Build fails from Node version mismatch → pin with
engines - Health check fails → forgot to bind $PORT in run command
- Client can't read environment variables → missing NEXT_PUBLIC_ prefix
Best Practices
Best practice for production Next.js on App Platform is to always enable output: 'standalone' in next.config.js because it noticeably reduces build artifact size and cold start time compared to bundling all node_modules.
Tune instance count to match actual workload — if your app relies on ISR or in-memory caching, start with 1 instance, then consider externalizing the cache layer to Managed Valkey Database before scaling to multiple instances to avoid showing inconsistent data.
Store your App Spec as YAML in your repository using doctl apps create --spec app.yaml or doctl apps update rather than configuring entirely through the Dashboard. This treats infrastructure as code, allowing you to version-control it alongside code and easily recreate or roll back environments.
Always separate secrets from plain environment variables by setting sensitive data (API keys, database URLs, auth secrets) as type Encrypted instead of Plain Text, and verify that no secrets accidentally got the NEXT_PUBLIC_ prefix.
Enable preview environments for every pull request so your team can review work on real URLs before merging instead of just reviewing code diffs — this catches UI/UX bugs and environment variable mistakes faster.
Finally, set up basic monitoring/alerting through App Platform's built-in features to notify you when memory usage or restart count spikes abnormally. These are early warnings that your current plan is too small for real traffic before users experience outages.
- Always enable
output: 'standalone'for production - Control instance count to match workload — especially important for cache-heavy apps
- Store App Spec as YAML in repo (infrastructure-as-code)
- Use Encrypted type for all secrets
Frequently Asked Questions
next start -p $PORT. Otherwise, health checks will fail.output: 'export') deploys as a Static Site component and qualifies for the free tier. SSR and API routes require Web Service deployment, which always costs at least $5/month for the 512 MiB RAM plan.