Self-Host NextJS in Fly.io: A Quick Try

DonvitoCodesDonvitoCodes
8 min read

Recently, Lee Robinson of Vercel gave a tutorial on how to self-host NextJS in a VPS. He used DigitalOcean. I also tried it in Hetzner using Ubuntu instance and it worked.

The example used Docker to deploy NextJS. It has all the dependencies like Nginx and a Postgres database, also run in Docker. It used docker-compose to run all the docker containers required for the demo.

I read the code and I saw that the Dockerfile was pretty straightforward. So I thought since it is just running in docker, it should work in fly.io too, so I tried. I made a simpler example with only the NextJS app and tried to deploy it in Fly.io.

Fly.io is a platform where you can deploy your app easily. I was also able deploy an API route within NextJS which is called by a page as a client component. This is just a quick test for simple apps. I am not sure if all NextJS features can be supported. As for this test, I just needed the API routes to work to see if server-side rendering works as well.

The NextJS App

I created a new NextJS app using this command.

npx create-next-app@latest

Just answer the questions and it’ll create a fresh NextJS app for you.

Add a new page in /src/app/blog/page.tsx

'use client';

import { useState, useEffect } from 'react';
import Image from 'next/image';
import Link from 'next/link';

interface BlogPost {
  id: number;
  title: string;
  content: string;
  author: string;
  date: string;
  image: string;
}

export default function BlogPage() {
  const [blogPosts, setBlogPosts] = useState<BlogPost[]>([]);

  useEffect(() => {
    async function fetchBlogPosts() {
      try {
        const response = await fetch('/api/blogs');
        if (!response.ok) {
          throw new Error('Failed to fetch blog posts');
        }
        const data = await response.json();
        setBlogPosts(data);
      } catch (error) {
        console.error('Error fetching blog posts:', error);
      }
    }

    fetchBlogPosts();
  }, []);

  return (
    <div className="container mx-auto px-4 py-8">
      <h1 className="text-3xl font-bold mb-8">Blog Posts</h1>
      <Link href="/" className="text-blue-500 hover:underline mb-4 inline-block">
        Back to Home
      </Link>
      {blogPosts.map((post) => (
        <article key={post.id} className="mb-8 p-6 bg-white rounded-lg shadow-md">
          <div className="flex flex-col md:flex-row">
            <div className="md:w-1/3 mb-4 md:mb-0 md:mr-6">
              <Image
                src={post.image || 'https://picsum.photos/600'}
                alt={post.title}
                width={100}
                height={100}
                className="w-full h-auto rounded-lg"
              />
            </div>
            <div className="md:w-2/3">
              <h2 className="text-2xl font-semibold mb-2">{post.title}</h2>
              <p className="text-gray-600 mb-4">
                By {post.author} on {new Date(post.date).toLocaleDateString()}
              </p>
              <p className="text-gray-800 mb-4">{post.content.slice(0, 200)}...</p>
              <Link href="https://donvitocodes.com" passHref>
                <span className="inline-block bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600 transition-colors duration-200">
                  Read More
                </span>
              </Link>
            </div>
          </div>
        </article>
      ))}
      <footer className="mt-8 text-center">
        <a href="https://donvitocodes.com" target="_blank" rel="noopener noreferrer" className="text-blue-600 hover:underline">
          Visit donvitocodes.com
        </a>
      </footer>
    </div>
  );
}

Then, add an API route which was called within the blog page.

// saved in src/app/api/blogs/route.ts

import { NextResponse } from 'next/server';

export const revalidate = 0;

// Static data for blog posts
const blogPosts = [
  {
    id: 1,
    title: 'Getting Started with Next.js',
    content: 'Next.js is a powerful React framework that enables you to build server-side rendered and statically generated web applications using React. It provides a great developer experience with features like automatic code splitting, optimized performance, and easy deployment. Whether you\'re building a simple blog or a complex web application, Next.js offers the tools and flexibility to create fast, scalable, and SEO-friendly websites. Did you know that Next.js was created by Vercel and first released in 2016? Since then, it has become one of the most popular React frameworks, used by companies like Netflix, TikTok, and Twitch.',
    author: 'DonvitoCodes',
    date: '2023-05-01',
    image: 'https://picsum.photos/600'
  },
  {
    id: 2,
    title: 'The Power of TypeScript',
    content: 'TypeScript adds static typing to JavaScript, bringing a new level of robustness and maintainability to your code. With TypeScript, you can catch errors early in the development process, improve code readability, and enhance IDE support. It\'s particularly beneficial for large-scale applications, where it can significantly reduce bugs and improve team collaboration. Learn how TypeScript can supercharge your JavaScript development and make your code more reliable and easier to refactor. Fun fact: TypeScript was developed by Microsoft and is used in many of their projects, including Visual Studio Code. It\'s estimated that using TypeScript can reduce bug density by up to 15%!',
    author: 'DonvitoCodes',
    date: '2023-05-15',
    image: 'https://picsum.photos/600'
  },
  {
    id: 3,
    title: 'Building APIs with Next.js',
    content: 'Next.js provides an easy way to create API routes, allowing you to build full-stack applications with ease. With API routes, you can handle server-side logic, interact with databases, and create RESTful endpoints all within your Next.js project. This seamless integration between frontend and backend makes Next.js an excellent choice for developers looking to build modern web applications. Discover how to leverage Next.js API routes to create powerful, efficient, and scalable APIs for your projects. Did you know that Next.js API routes support serverless functions out of the box? This means you can deploy your APIs without managing server infrastructure, leading to cost savings and improved scalability.',
    author: 'DonvitoCodes',
    date: '2023-06-01',
    image: 'https://picsum.photos/600'
  },
  {
    id: 4,
    title: 'Deploying Next.js with Fly.io',
    content: 'Fly.io is a platform for deploying Next.js apps that offers a seamless experience for developers. With Fly.io, you can easily deploy your Next.js applications globally, ensuring low latency and high availability for users around the world. This platform provides features like automatic SSL, custom domains, and easy scaling options. Learn how to leverage Fly.io\'s powerful infrastructure to deploy your Next.js applications quickly and efficiently, and discover best practices for optimizing your app\'s performance in a production environment. Interestingly, Fly.io uses a unique approach called "edge computing" to run your applications closer to your users, potentially reducing latency by up to 50% compared to traditional cloud hosting.',
    author: 'DonvitoCodes',
    date: '2024-10-13',
    image: 'https://picsum.photos/600'
  },
  {
    id: 5,
    title: 'The Rise of JAMstack',
    content: 'JAMstack, which stands for JavaScript, APIs, and Markup, is revolutionizing web development. This modern architecture pattern focuses on decoupling the frontend from the backend, resulting in faster, more secure, and highly scalable websites. By leveraging static site generators, serverless functions, and content delivery networks, JAMstack sites can achieve incredible performance and developer productivity. Did you know that sites built with JAMstack can be up to 10 times faster than traditional dynamic websites? Explore how JAMstack can transform your web development process and deliver lightning-fast experiences to your users.',
    author: 'DonvitoCodes',
    date: '2024-11-01',
    image: 'https://picsum.photos/600'
  },
  {
    id: 6,
    title: 'Mastering CSS Grid Layout',
    content: 'CSS Grid Layout is a powerful tool for creating complex, responsive layouts with ease. It introduces a two-dimensional grid system to CSS, allowing for more flexible and intuitive design possibilities. With CSS Grid, you can create magazine-style layouts, complex dashboard interfaces, and responsive designs without relying on external frameworks. A fascinating tidbit: CSS Grid was in development for nearly 20 years before being widely adopted by modern browsers in 2017. Learn how to harness the full potential of CSS Grid and revolutionize your approach to web layout design.',
    author: 'DonvitoCodes',
    date: '2024-11-15',
    image: 'https://picsum.photos/600'
  }
];

export async function GET() {
  console.log('API /blogs called at ' + new Date().toISOString());
  return NextResponse.json(blogPosts);
}

You can also clone my repo and just change the files for your purpose

Create a Dockerfile

Fly.io uses Docker to build and run your application. Here's an example Dockerfile for a Next.js app. I just copied this from Lee Robinson’s next-self-host demo.

FROM oven/bun:alpine AS base

# Stage 1: Install dependencies
FROM base AS deps
WORKDIR /app
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile

# Stage 2: Build the application
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN bun run build 

# Stage 3: Production server
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static

EXPOSE 3000
CMD ["bun", "run", "server.js"]

Deploying to Fly.io

1. Install the Fly CLI

If you haven't already, install the Fly CLI using Homebrew if you’re using a Mac. If you are using Linux or Windows, you can follow their guide https://fly.io/docs/flyctl/install/

brew install flyctl

3. Initialize Your Fly.io App

Navigate to your NextJS project directory and run:

fly launch

This command will guide you through creating a new app on Fly.io and generate a fly.toml configuration file.

4. Create a fly.io launch configuration

The fly.toml file is automatically created when you run

fly launch

It will generate this fly.toml configuration similar to this. This is how a fly.toml file looks. You can adjust configuration based on your needs.

# fly.toml app configuration file generated for nextjs-fly-polished-resonance-4569 on 2024-10-13T11:00:00+08:00
#
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
#

app = 'nextjs-fly-polished-resonance-4569'
primary_region = 'syd'

[build]

[http_service]
  internal_port = 3000
  force_https = true
  auto_stop_machines = 'stop'
  auto_start_machines = true
  min_machines_running = 0
  processes = ['app']

[[vm]]
  memory = '1gb'
  cpu_kind = 'shared'
  cpus = 1

5. Run fly deploy for rebuilding and deploying your code

fly deploy

Issues

Although the app is working fine, I am getting this error for the production deployment. It seems sharp hasn’t been installed properly. I’m still trying to figure it out. sharp is used for NextJS optimisation and is a required dependency. If you know the solution, you can comment below.

syd [info]API /blogs called at 2024-10-13T04:48:31.159Z
syd [info] ⨯ Error: 'sharp' is required to be installed in standalone mode for the image optimization to function correctly. Read more at: https://nextjs.org/docs/messages/sharp-missing-in-production
syd [info]API /blogs called at 2024-10-13T04:48:37.506Z

That’s it!

This is just a quick test to deploy simple NextJS apps to fly.io if you want to self-host it. I didn’t test all NextJS features so I this is not for production use yet.

1
Subscribe to my newsletter

Read articles from DonvitoCodes directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

DonvitoCodes
DonvitoCodes

I decided to take a career break and try out building apps using Generative AI. I am also learning NextJS and developing LaunchStack, starter code which I will use for my web projects