The Zero To Mastery Deno Course deploys a Deno docker container to Amazon EC2.
The EC2 instances cost money after you’ve exhausted your free 12 months. Plus, Amazon’s cloud services can be tricky. Sometimes, services spike. You quickly exceed your free tier, even for a simple toy app.
Let’s deploy Deno to Heroku instead. Heroku’s free tier stays free regardless of usage.
The free level is slow, because it will periodically stop to save resources. But for a toy app that’s not a problem.
Prerequisites
You’ll need a free Heroku account and the following programs on your local machine:
Fork and Clone the Starting Repository
Find the code for the Zero to Mastery Deno course. I have made a fork that I will use for this guide.
Make your own fork and clone it to your computer or just download the original source code.
The repository already contains a Dockerfile which we can use.
Prepare for Heroku
Heroku’s web dynos dynamically bind to a port variable. You have no control over which port your application will receive.
Our current implementation hard-codes the web port to 8000. That won’t fly with Heroku.
Thus, we have to make changes to our code.
(You can see all changes on GitHub.)
Adjust Dockerfile
Delete the last line: EXPOSE 8000
.
Change Drakefile.ts
Remove the hard-coded port and add a port as a flag:
desc("Run API");
task("start", [], async function () {
await sh(
- "PORT=8000 deno run --allow-env --allow-net --allow-read src/mod.ts",
+ "deno run --allow-env --allow-net --allow-read src/mod.ts --port=${PORT}",
);
});
Read flags
We need the flags
module from the standard library.
Inside src/deps.ts
:
// Standard library dependencies
export * as log from "https://deno.land/std@0.57.0/log/mod.ts";
+ export * as flags from "https://deno.land/std@0.57.0/flags/mod.ts"
The main program needs to read the port from the command flags (src/mod.ts
) 1.
- import { log, Application, send } from "./deps.ts";
+ import { flags, log, Application, send } from "./deps.ts";
import api from "./api.ts";
const app = new Application();
const PORT = 8000;
+ const argPort = flags.parse(Deno.args).port;
+ const port = argPort ? Number(argPort) : PORT;
+ if (isNaN(port)) {
+ log.error("Port is not a number.");
+ Deno.exit(1);
+ }
// more code
if (import.meta.main) {
- log.info(`Starting server on port ${PORT}...`);
+ log.info(`Starting server on port ${port}...`);
await app.listen({
- port: PORT,
+ port: port,
});
}
You can see all the changes on GitHub.
Heroku Setup
Open the terminal and log in:
heroku login
Create a new app:
heroku create
Build the Docker image and tag it with the Heroku registry format:
registry.heroku.com/<app>/web
For example,
docker build -t registry.heroku.com/red-goose-58626/web .
(Remember to use your app name.)
You can test your build locally:
docker run -it --rm -e PORT=8080 -p 8080:8080 registry.heroku.com/red-goose-58626/web:latest
(CTRL+C
will stop the docker container.)
Your Deno app should start and be available at http://localhost:8080
.
Push the image to the Heroku Docker registry:
docker push registry.heroku.com/red-goose-58626/web:latest
(Remember to use your app name.)
Deploy the docker image to Heroku now:
heroku container:release web
You can now reach your Deno application on the web via the Heroku URL, for example, https://red-goose-58626.herokuapp.com/index.html
Final Thoughts
Deploying a Deno Docker container is trivial after you’ve tackled the dynamic port binding.
I’ve used several resources for creating this blog post (see below). Credit goes to the original authors.
Links
- Deno: The Complete Guide Zero to Mastery (Udemy link) by Andrei Neagoie, Adam Odziemkowski
- Heroku Buildpack for Deno by Tomofumi Chiba
- Test-Driven Development with Python, Flask, and Docker by Michael Herman
- Fork of the Deno course repository
I copied the implementation from the Heroku buildpack for Deno. ↩︎