Skip to content

This Dot Blog

This Dot provides teams with technical leaders who bring deep knowledge of the web platform. We help teams set new standards, and deliver results predictably.

Newest First
Tags:JavaScript
Deploying Next.js Applications to Fly.io cover image

Deploying Next.js Applications to Fly.io

Fly.io has gained significant popularity in the developer community recently, particularly after being endorsed by Kent C. Dodds for hosting his Epic Web project. It's a go-to choice for hobby projects, thanks to its starting plans that are effectively free, making it highly accessible for individual developers. While Next.js applications are often deployed to Vercel, Fly.io has emerged as a perfectly viable alternative, offering robust hosting solutions and global deployment capabilities. In this blog post, we'll give an overview of how to install a Next.js app to Fly.io, mentioning any gotchas you should be aware of along the way. The Project Our project is a simple Next.js project using the latest version of Next.js at the time of the writing (14). It uses the app directory and integrates with Spotify to get a list of episodes for our podcast, Modern Web. The bulk of the logic is in the page.tsx file, shown below, which represents the front page that is server-rendered after retrieving a list of episodes from Spotify. ` The getEpisodes is a custom function that is a wrapper around the Spotify API. It uses the Spotify client ID and secret (provided through the SPOTIFY_CLIENT_ID and SPOTIFY_CLIENT_SECRET environment variables, respectively) to get an access token from Spotify and invoke a REST endpoint to get a list of episodes for the given show ID. As can be seen from the above code, the Home is an async, server-rendered component. Scaffolding of Fly.io Configuration To get started with Fly.io and deploy a new project using flyctl, you need to go through a few simple steps: installing the flyctl CLI, logging into Fly.io, and using the flyctl launch command. Installing the CLI Installing flyctl is different depending on the operating system you use: - If you're on Windows, the easiest way to install flyctl is by using scoop, a command-line installer. First, install scoop if you haven’t already, then run scoop install flyctl in your command prompt or PowerShell. - For macOS users, you can use Homebrew, a popular package manager. Simply open your terminal and run brew install superfly/tap/flyctl. - Linux users can install flyctl by running the following script in the terminal: curl -L https://fly.io/install.sh | sh. This will download and install the latest version. Logging In After installing flyctl, the next step is to log in to your Fly.io account. Open your terminal or command prompt and enter flyctl auth login. This command will open a web browser prompting you to log in to Fly.io. If you don’t have an account, you can create one at this step. Once you're logged in through the browser, the CLI will automatically be authenticated. Scaffolding the Fly.io Configuration The next step is to use fly launch to add all the necessary files for deployment, such as a Dockerfile and a fly.toml file, which is the main Fly.io configuration file. This command initiates a few actions: - It detects your application type and proposes a configuration. - It sets up your application on Fly.io, including provisioning a new app name if you don’t specify one. - It allows you to select a region to deploy to. There are really many to choose from, so you can get really picky here. Once the process completes, flyctl will be ready for deploying the application. In our case, the process went like this: ` Deploying Now, if this was a simpler Next.js app without any environment variables, running flyctl deploy would build the Docker image in a specialized "builder" app container on Fly.io and deploy that image to the app container running the app. However, in our case, executing flyctl deploy will fail: ` This is because our page is statically rendered, and the Next.js build process attempts to run Home, our server-rendered component to cache its output. Before we can do this, we need to add our environment variables so that Fly.io is aware of them, but this is somewhat a tricky subject, so let's explain why in the following chapter. Handling of Secrets Most complex web apps will need some secrets injected into the app via environment variables. Environment variables are a good way to inject sensitive information, such as API secret keys, to your web app without storing them in the repository, the file system, or any other unprotected place. Unlike other providers such as the previously mentioned Vercel, Fly.io distinguishes built-time and run-time secrets, which are then injected as environment variables. Build-time secrets are those secrets that your app requires to build itself, while run-time secrets are needed while the app is running. In our case, due to the fact that Next.js will attempt to cache our static pages upfront, the Spotify client ID and client secret are needed during both build-time and run-time (after the cache expires). Build-Time Secrets Our Next.js app is built while building the Docker image, therefore build-time secrets should be passed to the Docker context. The recommended, Docker-way of doing this, is through Docker's build-time secrets, which are added through a special --mount=type=secret flag to the RUN command that builds the site. This is a relatively newer feature that allows you to securely pass secrets needed during the build process without including them in the final image or even as an intermediate layer. This means that, instead of having the following build command in our Dockerfile: ` we will have: ` Now, you can either modify the Dockerfile manually and add this, or you can use a helpful little utility called dockerfile: ` If we were using docker build to build our image, we would pass the secret values like so: ` However, in our case we use fly deploy, which is just a wrapper around docker build. To pass the secrets, we would use the following command: ` And now, the app builds and deploys successfully in a few minutes. To summarize, if you have any secrets which are necessary at build-time, they will need to be provided to the fly deploy command. This means that if you have a CI/CD pipeline, you will need to make sure that these secrets are available to your CI/CD platform. In the case of GitHub actions, they would need to be stored in your repository's secrets. Run-Time Secrets Run-time secrets are handled in a different way - you need to provide them to Fly.io via the fly secrets set command: ` Now, you might be wondering why fly deploy cannot use these secrets if they are already stored in Fly.io. The architecture of Fly.io is set up in such a way that reading these secrets through the API, once they are set, is not possible. Secrets are stored in an encrypted vault. When you set a secret using fly secrets set, it sends the secret value through the Fly.io API, which writes it to the vault for your specific Fly.io app. The API servers can only encrypt; they cannot decrypt secret values. Therefore, the fly deploy process, which is, if you remember, just a wrapper around docker build, cannot access the decrypted secret values. Other Things to Consider Beware of .env Files In Next.js, you can use .env as well as .env.local for storing environment variable values for local development. However, keep in mind that only .env.local files are ignored by the Docker build process via the .dockerignore file generated by Fly.io. This means that if you happen to be using an .env file, this file could be bundled into your Docker image, which is potentially a security risk if it contains sensitive information. To prevent this from happening, be sure to add .env to your .dockerignore file as well. Not Enough Memory? For larger Next.js sites, you might run into situations where the memory of your instance is simply not enough to run the app, especially if you are on the hobby plan. If that happens, you have two options. The first one does not incur any additional costs, and it involves increasing the swap size. This is not ideal, as more disk operation is involved, making the entire process slower, but it is good enough if you don't have any other options. To set swap size to something like 512 MB, you need to add the following line to the fly.toml file near the top: ` The second one is increasing memory size of your instance. This does incur additional cost, however. If you decide to use this option, the command to use is: ` For example, to increase RAM memory to 1024 MB, you would use the command: ` After making the changes, you can try redeploying and seeing if the process is still crashing due to lack of memory. Conclusion In conclusion, deploying Next.js applications to Fly.io offers a flexible and robust solution for developers looking for alternatives to more commonly used platforms like Vercel. We hope this blog post has provided you with some useful insights on the things to consider when doing so. Be sure to also check out our Next starter templates on starter.dev if you'd like to integrate a few other frameworks into your Next.js project. The entire source code for this project is available on Stackblitz....

New Core Web Vitals and How They Work cover image

New Core Web Vitals and How They Work

A guide to INP, the new Core Web Vital coming in March 2024. The what, why and how....

Exploring the Hidden Gems of the Next Image Component: What You Might Be Overlooking cover image

Exploring the Hidden Gems of the Next Image Component: What You Might Be Overlooking

A blog post that explores hidden features that are easy to overlook...

A Solid(JS) Developer Experience with Atila Fassina, Solid DX Team cover image

A Solid(JS) Developer Experience with Atila Fassina, Solid DX Team

Rob Ocel sits down with Atila Fassina, a Developer Relations Engineer at Crabbula and a member of the Developer Experience team for SolidJS. Atila's involvement with SolidJS began after a conversation with Ryan Carniato, the creator of the framework. He was immediately drawn to the framework's plans and its focus. Atila expresses his excitement about the upcoming release of SolidJS and Tauri V2. Atila and Rob discuss the significance of comprehensive documentation in software development. Atila emphasizes the need for well-written documentation that caters to both beginners and experienced developers. He mentions the ongoing efforts to improve the documentation for SolidJS, providing developers with the resources they need to understand the framework and troubleshoot issues. The conversation also explores the challenges of introducing new concepts and paradigms to the developer community. Listen to the full podcast episode here!...

It's Impossible For This Code to Fail - with Loren Sands-Ramshaw  cover image

It's Impossible For This Code to Fail - with Loren Sands-Ramshaw

Loren Sands-Ramshaw, Developer Relations Engineer at Temporal joins Rob Ocel to talk about reliable application development. They introduce the topic of durable execution and talk about reliability in systems, unraveling common issues developers face and showcase the benefits that durable execution can bring to software development. They also talk about the challenges of traditional programming and the complexities of event-driven architecture. Listen to the full podcast here: https://modernweb.podbean.com/e/modern-web-podcast-s11e19-its-impossible-for-this-code-to-fail/...

OAuth2 for JavaScript Developers cover image

OAuth2 for JavaScript Developers

Using GitHub as an example, the post guides JavaScript developers through the OAuth2 process, emphasizing its importance for secure and efficient third-party integrations in web applications....

Astro: Do You Even Need JavaScript? with James Quick cover image

Astro: Do You Even Need JavaScript? with James Quick

James Quick, content creator and co-host of the Compressed FM podcast talks about the evolution of Astro, a powerful static site generator that we should all get familiar with. He talks in depth about the framework and where the framework may head in the future. Astro has been making waves in the web development community. It has evolved over time, and is now competing with major meta frameworks. But with its unique features and excellent developer experience, Astro offers developers a fresh perspective on building websites and applications. Astro v3 brings a host of exciting features to the table. James shares Astro supports the View Transition API, which allows for smoother page transitions and a seamless user experience. He and Dustin talk about Astro's image optimization component, which optimizes images for performance without compromising quality. One of the standout features of Astro is its island architecture. James explains how this architecture enables developers to seamlessly integrate other frameworks, providing unparalleled flexibility and versatility in web development. This unique approach empowers developers to leverage the strengths of multiple frameworks and create truly dynamic and powerful websites. James shares his thoughts on Astro’s new Qwik integration, highlighting its potential impact on web development workflows. Qwik integration opens up new possibilities and advantages for developers, streamlining their development process and enabling them to build faster and more efficiently. As web development continues to evolve, Astro stands as a powerful tool for developers seeking flexibility, performance, and enhanced user experiences. Listen to the full podcast episode here: https://modernweb.podbean.com/e/jamesquick/...

How to add Dark Mode in your Web Application  cover image

How to add Dark Mode in your Web Application

Learn how to enable persistent Light / Dark Theme toggling on your website using CSS and Vanilla JS....

Nuxt DevTools v1.0: Redefining the Developer Experience Beyond Conventional Tools cover image

Nuxt DevTools v1.0: Redefining the Developer Experience Beyond Conventional Tools

Nuxt DevTools v1.0 is a game-changer for web developers, featuring integrated VS Code, real-time views, and a customizable UI. This new toolset enhances efficiency, revolutionizing web development....

Understanding Vue's Reactive Data cover image

Understanding Vue's Reactive Data

A blog post that unravel how Reactivity works in Vue 3...

Angular 17: Continuing the Renaissance cover image

Angular 17: Continuing the Renaissance

Dive into the Angular Renaissance with Angular 17, emphasizing standalone components, enhanced control flow syntax, and a new lazy-loading paradigm. Discover server-side rendering improvements, hydration stability, and support for view transitions....

Bun v1.0 cover image

Bun v1.0

On September 8, 2023, Bun version 1 was released as the first production-ready version of Bun, a fast, all-in-one toolkit for running, building, testing, and debugging JavaScript and TypeScript. Why a new JS runtime You may ask, we already have Node and Deno, so why would we need another javascript runtime, Well yes we had Node for a very long time, but developers face a lot of problems with it, and maybe the first problem is because it’s there for a very long time, it has been changing a lot between different versions and one of the biggest nightmares for JavaScript developers these days is upgrading the node version. Also, Node lacks support for Typescriptt. Zig programming language One of the main reasons that Bun is faster than Node, is the programming language it has been built with which is Zig. Zig is a very fast programming language, even faster than C) (here is some benchmarks), it focuses on performance and memory control. The reasons it’s faster than C is because of the LLVM optimizations it has, and also the way it handles the undefined behavior under the hood Developer Experience Bun delivers a better developer experience than Node on many levels. First, it’s almost fully compatible with Node so you can use Node packages without any issues. Also, you don’t need to worry about JS Common and ES Modules anymore, you can use both in the same file, yup you read that right, for example: ` Also, it has a built-in test framework similar to Jest or Vitest in the project so no need to install a different test framework with different bundler in the same project like Webpack or Vite ` Also, it supports JSX out-of-the-box ` Also, Bun has the fastest javascript package manager and the most efficient you can find as of the time of this post ` Bun Native APIs Bun supports the Node APIs but also they have fun and easy APIs to work with like * Bun.serve() : to create HTTP server * Bun.file() : to read and write the file system * Bun. password.hash(): to hash passwords * Bun.build(): to bundle files for the browser * Bun.FileSystemRouter(): a file system router And many more features Plugin system Bun also has an amazing plugin system that allows developers to create their own plugins and add them to the Bun ecosystem. ` Conclusion Bun is a very promising project, and it’s still in the early stages, but it has a lot of potential to be the next big thing in the JavaScript world. It’s fast, easy to use, and has a lot of amazing features. I’m very excited to see what the future holds for Bun and I’m sure it will be a very successful project....