Azri Ismail
← Back to Blog

Deploying a Next.js 15 Blog to DigitalOcean App Platform: A Complete Guide

•6 min read

Building a personal blog is one thing, but deploying it to production where the world can see it? That's where the real adventure begins. Today, I'll walk you through exactly how I deployed my Next.js 15 blog to DigitalOcean App Platform, including all the gotchas and solutions I encountered along the way.

The Tech Stack

Before diving into deployment, here's what we're working with:

  • Frontend: Next.js 15 with App Router
  • Database: Supabase (PostgreSQL)
  • Authentication: WorkOS AuthKit
  • Styling: Tailwind CSS
  • Deployment: DigitalOcean App Platform

Why DigitalOcean App Platform?

I chose DigitalOcean App Platform for several reasons:

  • Simplicity: Git-based deployments with automatic builds
  • Cost-effective: Starting at $5/month for small apps
  • Integrated: Built-in monitoring, logging, and scaling
  • Developer-friendly: Great documentation and support

The Deployment Process

Step 1: Preparing the Repository

First, I needed to ensure my code was production-ready. This meant adding some configuration files:

Next.js Configuration (next.config.ts):

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  output: 'standalone',
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: '**',
      },
    ],
  },
  eslint: {
    ignoreDuringBuilds: true,
  },
};

export default nextConfig;

The output: 'standalone' is crucial for App Platform deployments as it creates a self-contained application.

Step 2: Handling Dependency Conflicts

Here's where I hit my first major roadblock. Next.js 15 and WorkOS AuthKit have a peer dependency conflict:

ERESOLVE could not resolve
While resolving: @workos-inc/[email protected]

WorkOS AuthKit currently supports Next.js 13.5.4 || ^14.0.3, but I was using Next.js 15.4.5.

Solution: I created an .npmrc file to handle this:

legacy-peer-deps=true
strict-peer-deps=false

This tells npm to use the older, more permissive dependency resolution algorithm.

Step 3: Creating the App Platform Configuration

I created a .do/app.yaml file to define the deployment configuration:

name: next-blog
services:
- name: web
  source_dir: /
  github:
    repo: your-username/next-blog
    branch: main
  run_command: npm start
  build_command: npm install --legacy-peer-deps && npm run build
  environment_slug: node-js
  instance_count: 1
  instance_size_slug: basic-xxs
  routes:
  - path: /
  env:
  - key: NODE_ENV
    value: production
  # ... other environment variables

Notice the --legacy-peer-deps flag in the build command - this was essential for resolving the dependency conflicts.

Step 4: Fixing the Marked Library Issue

During the build process, I encountered another TypeScript error:

Type error: Object literal may only specify known properties, 
and 'highlight' does not exist in type 'MarkedOptions'.

The marked library had updated its API, removing the highlight option. I had to refactor my markdown renderer:

Before:

marked.setOptions({
  highlight: function(code, lang) {
    if (lang && Prism.languages[lang]) {
      return Prism.highlight(code, Prism.languages[lang], lang)
    }
    return code
  },
});

After:

const renderer = new marked.Renderer()
renderer.code = function(code, language) {
  const validLanguage = language && Prism.languages[language] ? language : 'text'
  const highlightedCode = Prism.languages[validLanguage] 
    ? Prism.highlight(code, Prism.languages[validLanguage], validLanguage)
    : code
  
  return `<pre><code class="language-${validLanguage}">${highlightedCode}</code></pre>`
}

marked.setOptions({
  renderer: renderer,
})

Step 5: Setting Up Environment Variables

One of the biggest differences between local development and production is environment variable management. In DigitalOcean App Platform, you set these through the dashboard:

  1. Go to Apps → Select your app
  2. Navigate to Settings → Environment Variables
  3. Add all required variables:
NODE_ENV=production
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
SUPABASE_SERVICE_ROLE_KEY=your_supabase_service_role_key
WORKOS_API_KEY=your_workos_api_key
WORKOS_CLIENT_ID=your_workos_client_id
WORKOS_COOKIE_PASSWORD=32_character_random_string
WORKOS_REDIRECT_URI=https://your-app.ondigitalocean.app/api/auth/callback
NEXT_PUBLIC_APP_URL=https://your-app.ondigitalocean.app
NEXT_PUBLIC_BLOG_TITLE=Your Blog Title
NEXT_PUBLIC_BLOG_DESCRIPTION=Your blog description

Pro tip: Generate a strong 32-character string for the cookie password. Security matters!

Step 6: Updating External Services

Don't forget to update your external services with the new production URLs:

Supabase:

  • Add your app domain to allowed origins
  • Update any hardcoded URLs in your RLS policies

WorkOS:

  • Add your production domain as an allowed redirect URI
  • Update the callback URL to match your deployment

Step 7: Database Setup

I had to run several SQL scripts in Supabase to set up the production database:

  1. Initial schema (supabase/schema.sql)
  2. Updated policies (supabase/updated-policies.sql)
  3. Hero content fields (supabase/add-hero-content.sql)

This ensures the production database has all the necessary tables and security policies.

The Deployment Experience

Once everything was configured, the actual deployment was surprisingly smooth:

  1. Push to GitHub: All changes committed and pushed
  2. Create App: Connected GitHub repository to DigitalOcean
  3. Configure Build: Set build commands and environment variables
  4. Deploy: Watched the magic happen in real-time

The build took about 5-7 minutes, and then my blog was live on the internet!

Post-Deployment Tasks

After deployment, there were a few housekeeping tasks:

  1. Admin Setup: Visited /admin-login to create my admin account
  2. Profile Configuration: Set up my avatar and hero content
  3. Test Post: Created a test blog post to verify everything worked
  4. DNS Setup: (Optional) Configure custom domain

Performance and Monitoring

DigitalOcean App Platform provides excellent monitoring out of the box:

  • Real-time logs: Perfect for debugging
  • Performance metrics: CPU, memory, and request metrics
  • Automatic scaling: Handles traffic spikes gracefully
  • SSL certificates: Automatic HTTPS with Let's Encrypt

Cost Breakdown

Here's what it costs to run this setup:

  • DigitalOcean App Platform: $5/month (Basic plan)
  • Supabase: Free tier (sufficient for personal blogs)
  • WorkOS: Free tier (up to 7,500 MAUs)
  • Domain: ~$10/year (optional)

Total: About $5/month for a fully-featured, production-ready blog.

Lessons Learned

  1. Dependency Management: Always check peer dependencies when using bleeding-edge versions
  2. Environment Variables: Production environment setup is crucial - test early
  3. Documentation: Keep detailed deployment notes for future reference
  4. Monitoring: Set up alerts from day one
  5. Backups: Supabase handles this, but verify your backup strategy

Common Gotchas and Solutions

Build Fails with ERESOLVE:

  • Use --legacy-peer-deps in build command
  • Add .npmrc file with legacy peer deps enabled

Authentication Issues:

  • Double-check WorkOS redirect URIs
  • Ensure HTTPS in production
  • Verify cookie settings

Database Connection Issues:

  • Check Supabase allowed origins
  • Verify environment variables
  • Review RLS policies

Final Thoughts

Deploying a modern Next.js application doesn't have to be complicated. DigitalOcean App Platform provides an excellent balance of simplicity and power, making it perfect for personal projects and small applications.

The key is preparation: understanding your dependencies, configuring your environment properly, and having a clear deployment checklist. Once you've done it once, subsequent deployments become almost trivial.

Would I recommend this stack? Absolutely. The developer experience is fantastic, the costs are reasonable, and the performance is excellent. Plus, having your own corner of the internet to share your thoughts and projects? Priceless.

Resources


This blog post is written on the very platform I just deployed. Meta? Perhaps. Satisfying? Definitely.

What's your experience with Next.js deployments? Have you tried DigitalOcean App Platform? Let me know in the comments below!

Loading comments...