Deno on Fly using Buildpacks
Fly.io
Posted on May 5, 2020
We really like Deno at Fly.io. It really is a better Node and we’d love people to build more with it. Our plan to aid in the adoption of Deno? “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@0.11.0/mod.ts";
app(
get("/hello", () => `Hello`),
get("/hello/:id", ({ params }) => `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.
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.
Posted on May 5, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.