Shipping Deno on Fly.io using Buildpacks

| ,

2

We really like Deno at Fly. It really is a better Node and we’d love people to build more with it. Our plan? “Let’s make building and deploying Deno apps on Fly as simple as other languages”. Now, you can configure, build and deploy Deno code in just two commands, and we’d like to show you how we did it.

What’s Deno?

The deno.land website bills Deno as a secure runtime for JavaScript and TypeScript. In practice, it’s like working with a streamlined Node where TypeScript is the norm, with a solid Rust foundation. It also incorporates many of the lessons learned over Node’s development and growth.

As we write this, Deno is heading rapidly towards its first 1.0 release candidate and we are really looking forward to a post-1.0 Deno. We believe as more people discover Deno’s elegance as a platform it’ll become much more popular. So how do you build a Deno app for Fly now?

Straight to the chase

  • Name your app’s entry point server.ts
  • Create your Fly app with flyctl apps create --builder flyio/builder
  • flyctl deploy and watch it all build and deploy.

That’s it.

Behind the builder

The “chase” above was notably short. How? Let’s look at what’s powering this build process. Fly’s builder support landed in early April and is based on the Cloud Native Buildpacks.io (CNB) specifications and implementations for building cloud native container images.

A Cloud Native build is made up of stacks, builders and buildpacks which come together to make a repeatable build process, for different languages and frameworks. Used together, they deliver container images that are ready to run. For the Deno builder, we created our own stack, builder and buildpack to build Deno.

Stacks

Everything in CNB is built on a “stack”, the base operating system in which the builders operate. For the Fly stack, we selected Ubuntu bionic, added in curl and unzip utilities (to support the Deno installer) and built the three images – base, build and run – which will be used in the subsequent steps.

Builders

A builder in CNB is a container loaded with all the OS level tooling needed to construct an image. It’s fundamentally an OS image of some form. It’s not tied to any specific container platform such as Docker, it’s designed to run wherever you can run a container.

That said, the Buildpacks.io developers do have the pack commmand that uses Docker to run these containers – especially useful on Windows and macOS where there is no native Linux container support.

The builder brings up a container running the build stack and then steps through the buildpacks associated with the builder to work out which build script to run in that container.

Buildpacks

The buildpacks are smaller bundles of scripts (or Go programs) which have two jobs, detect and build.

detect

The detect script tries to work out if the directory contents it has been given are appropriate to build with its build script. For example, a detect script for Ruby would look for a Gemfile and go “aha! this is the song of my people! run my build script”.

Now, Deno doesn’t have an obvious packaging file like Ruby or well, anything else, due to it being so self-contained. So, we made a rule. If there’s a server.ts file in the directory, we’ll assume it’s a Deno application and signal to run our build script.

build

The build script runs in the Builder’s container and does the work of assembling tools and building the layers to create our image. In the case of our Deno buildpack, that includes downloading Deno into the image and running the Deno deno cache command so all the dependencies are ready to run. Finally, it writes out a set of launch commands to start up the application.

permissions

Now, one thing Deno does is restrict access to resources by default. You have to specify which resources are available to any program with --allow-* command-line flags. Of course this is hard to pass through the command chain, so we don’t. What we do is add a .permissions file, containing the command-line arguments you’d expect to be added to the command-line, to the directory with the code we’re building. In the example app, this file contains:

--allow-env --allow-net

Allowing environment variable and network access. If there’s no .permissions file, currently we default to no permission flags, in sync with the default Deno experience. You can incrementally add appropriate permissions to your app as you iterate your code.

If you want to allow all access, you can put -A into .permissions, allow all access and sort out permissions later (well, that’s what you’ll say but you know it’ll slip and you’ll do a production deployment with it still in place.

The arguments are added to the deno command when the container and its launch file are built.

Step by Step

Creating an app

So, let’s build a Deno app. We’re going to use dinatra, a Deno module which gives Sinatra-like capabilities to Deno apps. You’ll of course want to install Deno and create a directory for your app. Then make a server.ts file and put this in it:

import {
  app,
  get,
  post,
  redirect,
  contentType,
} from "https://denopkg.com/syumai/dinatra/mod.ts";

const greeting = "<h1>Hello From Deno on Fly!</h1>";

app(
  get("/", () => greeting),
  get("/:id", ({ params }) => greeting + `</br>and hello to ${params.id}`),
);

And that’s an entire simple web server application. Don’t forget permissions though: create a .permissions file with --allow-net in it; all this application wants is network access.

Extra steps

You may want to test your app before deploying it. You have two options.

The first is to just run it before packaging it into a container image.

Running the deno command with the permissions:

deno --allow-net server.ts

will be all you need in that case

The second is to package up the container image and run that image. You’ll need Docker and Buildpacks.io’s pack installed locally to do this. Use pack to create the image:

pack build test-server-app --builder flyio/builder

Then use Docker to run the test-server-app image:

docker run -p 8080:8080 test-server-app

Deploying to Fly

Now we can create a Fly app for this code by running

flyctl apps create --builder flyio/builder

And we can deploy it with flyctl deploy

What Next

For Fly Buildpacks

We’ve made all the Buildpack code available in a GitHub repository for anyone who wants to improve the process. We’ll be looking to add new buildpacks to it too, and enhance existing buildpacks. It’s worth noting that you can build your own local buildpack and use it with the fly-builder stack for local/test builds – you’ll have to publish it with public access if you want to do Fly deployments with it though.

For Deno on Fly

We’re ready for your next Deno app on Fly, even if it’s your first. With a simple build and deploy process, it’s easier than ever.

This article originally appeared on the Fly.io Blog: https://fly.io/blog/deno-on-fly-using-buildpacks/

Previous

45 Useful JavaScript Tips, Tricks and Best Practices

Should You Still Learn Vue 2 Or Just Wait For The Release Of Vue 3?

Next

Leave a Comment