Fly.io ❤️ JS

A ballon and the JS logo holding hands and having a picnic lunch
Image by Annie Ruygt

Fly.io is a great place to run fullstack applications. For most programming languages, there is a defacto default fullstack framework. For Ruby, there is Rails. For Elixir, there is Phoenix. For PHP there is Laravel. For Python, there is Django.

If you don’t know where to look, Node.js appears to be a mess. For starters there are plenty of js frameworks to choose from. Then there are three different package managers. Not to mention that Typescript as an alternative to JavaScript. And if that is not bad enough Bun and Deno are providing alternatives to Node itself.

The result is predictable. Fly.io has a number of community contributed templates for a small number of Node frameworks. Some have had more attention than others.

It is time to clean up the mess.

package.json enters the chat

The key sentence in the preceding section starts with If you don’t know where to look. The right place to start is package.json. It tells you what dependencies need to be installed. For most frameworks, it tells you how to start the web server. And if there is a build step. And if there are any development dependencies that may be needed to run the build, and removed prior to deployment.

Given this knowledge, a baseline Dockerfile can be built for any framework that follows these conventions. Handling different package managers can be accomplished by looking for yarn.lock and pnpm-lock.yaml files. TypeScript is a devDependency and handled by the build step. While Deno projects don’t typically have package.json files, some bun projects do.

The dockerfile-node project endeavors to do exactly that:

npx @flydotio/dockerfile

This will create (or replace!) your existing Dockerfile, as well as ensure that you have a .dockerignore file, and optionally may create a docker-entrypoint script. You can run with this Dockerfile locally, or use it to deploy on your favorite cloud provider. For Fly.io, you would get started by running:

fly launch --dockerfile Dockerfile

The --dockerfile parameter is needed to tell fly launch to use your Dockerfile rather than trying to generate a new one.

Of course, if you prefer to run your application on Google Cloud Run, Amazon ECS, MRSK, or even locally, you are welcome to do so.

You can play with this right now.

Deploy using [Fly.io terminal](https://fly.io/terminal) or see our [Hands-on](https://fly.io/docs/hands-on/) guide that will walk you through the steps.

Try Fly for free

Devils in the details

Not all frameworks are alike.

Some will, by default, start servers that only process requests that come from the localhost. That, of course, is entirely unsatisfactory.

Some require extra steps, for example applications that make use of Prisma.

One (and I won’t mention the name) actually lists the package needed to run the production server as a development only dependency.

Fortunately, ejs templates can include if statements and/or make use of computed variables that customize the Dockerfiles produced.

As a starter set, I’ve got templates working for the following frameworks: Express, Fastify, Gatsby, Nest, Next, Nuxt, and Remix. At the moment, I’ve been focusing on breadth vs depth, so what I have working may not be able to handle much more than the splash screen, but my experience is that getting that far is often the hardest part, after that point you have all the scaffolding in place and can focus on any specific issue that may come up.

Those are the successes so far. Here’s a list of frameworks that are still being worked on, along with the current blocking issue:

  • tRPC: Access to the Postgres database is required during the build step. Worst case, we do the build step during the deployment of the server, but that is suboptimal for cases where multiple servers are started.
  • Strapi: Needs to set secrets for JWT, session. This isn’t a problem, and already is solved for Remix deployment for fly, but at the moment goes beyond what a Dockerfile generator can do by itself.
  • RedwoodJS: No scripts, recommends nginx. Fly.io already has a template for Redwood, so it presumably is just a matter of work to figure out how to fit the steps required into the general purpose template. It may make sense to either encourage Redwood to add scripts to their package.json, or to add them during the dockerfile generation. If not, if statements can be used to generate Redwood-specific steps rather than generic ones.
  • SvelteKit: attempting to deploy results in Could not detect a supported production environment. Again, just appears to be a matter of work to add a new production environment.
  • KeystoneJS: at build time, I’m seeing ✘ [ERROR] Could not resolve "./keystone". Works fine on development machine, so I probably just missed a step.

In the fullness of time, these will be picked off one by one. This code is all open source, so everybody with an interest in a particular framework can contribute via issues and pull requests. Interest and participation will definitely affect prioritization of this work.

Futures

Once this script has a little bit of exposure to real world usage, it will replace the existing flyctl scanners, much in the way that dockerfile-rails is the basis for the Dockerfiles produced for Rails applications with Fly.io. At which point, usage will be as simple as fly launch.

Integration with fly launch will also enable thing like setting of secrets, defining volumes, launching of databases, and defining health checks as part of the workflow.

This package will also be designed to be re-run and accept arguments which will customize the Dockerfile produced. Peruse the usage for dockerfile-rails to see examples of the types of customizations possible. Some highlights:

  • --cache - use build caching to speed up builds
  • --swap=n - allocate swap space enabling running of larger applications on memory constrained VMs.
  • --compose - generate a docker-compose.yml file

The scanner will also be able to do things like detect the inclusion of puppeteer and automatically install and configure Chrome/Chromium. This is already being done for Rails applications today.

Another thing already being done for Rails applications is to run the web server as a non-root user for security reasons. Repeating this for Node.js will require knowledge of what files the application is expected to write to and which are expected to be read-only. This knowledge is necessarily framework specific, and may not be possible for minimal and general purpose frameworks like express.

Got Feedback?

If you have questions, comments, or concerns, let us know!

If they are even vaguely Fly.io related, feel free to use our community forum. Otherwise, start a discussion on GitHub.

And to those that wish to contribute, perhaps to make support for their favorite framework(s) better…. let’s do this!