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.
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 adocker-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!