Deploy a Deno App

Deno (opens in a new tab) is a next-generation runtime for JavaScript and TypeScript projects with a modern feature set focused on security, developer experience, and performance.

This guide explains how to deploy a Deno application to Koyeb using Git-driven deployment. The application can be built using either native buildpacks or from a Dockerfile.

To successfully follow this documentation, you will need to have a Koyeb account (opens in a new tab). You can optionally install the Koyeb CLI if you prefer to follow this guide without leaving the terminal.

You can deploy and preview the Deno application from this guide using the Deploy to Koyeb button below.

Deploy to Koyeb (opens in a new tab)

You can consult the repository on GitHub (opens in a new tab) to find out more about the example application that this guide uses.

Create the Deno app

To create a basic Deno application, you'll need to install Deno (opens in a new tab) on your local computer.

To begin, create and navigate to a new directory for your Deno project:

mkdir example-deno
cd example-deno

Next, create a main.ts file to define our simple Deno application. Paste the following contents within:

main.ts
const port = parseInt(Deno.env.get('PORT') ?? '8000')
 
function hello_world(_req: Request): Response {
  return new Response('Hello, world!')
}
 
Deno.serve({ port: port }, hello_world)

The application starts a basic web server that responds to all requests with a "Hello, world!" message. The server uses the PORT environment variable to determine which port to listen on and falls back on port 8000 if the variable is undefined.

Test the Deno app locally

You can run the application by typing:

deno run main.ts

Since the application references the PORT environment variable, Deno will prompt you to allow access to the environment. Type "y" to allow. It will then prompt you to confirm that it can serve the application with network access. Again, type "y":

┌ ⚠  Deno requests env access to "PORT".
├ Run again with --allow-env to bypass this prompt.
└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all env permissions) >️ y
┌ ⚠  Deno requests net access to "0.0.0.0:8000".
├ Requested by `Deno.listen()` API.
├ Run again with --allow-net to bypass this prompt.
└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all net permissions) > y

As the message indicates, you can provide the --allow-env and --allow-net options to avoid these prompts in future invocations.

Once you confirm access with both prompts, you can visit localhost:8000 in your web browser to access the application. You should see the "Hello, world!" message from the application.

When you are finished, press CTRL-C to stop the server.

Add project metadata files

Next, we need to add the files the project metadata files. These are used to manage dependencies, define scripts, and more.

If you want to deploy using the Node.js buildpack (opens in a new tab), you'll need to create a package.json file. Koyeb's build process uses that file as an identifier for Node.js-compatible projects. We will list the deno-bin package (opens in a new tab) as a dependency to install Deno within the environment.

First, find the latest version of Deno by typing:

curl --head https://github.com/denoland/deno/releases/latest | grep location | grep --only-matching '[^/]*$'

You'll see a response that looks something like this:

v1.39.0

We will ask for this version or later in our dependency list.

In the project root, create a new file called package.json with the following contents. Be sure to adjust the ^1.39.0 line to the version line you found above, prefixed by the ^ character:

package.json
{
  "name": "Deno example application",
  "scripts": {
    "dev": "deno run --watch --allow-net --allow-env main.ts",
    "build": "deno compile --allow-net --allow-env --output=example-deno main.ts",
    "start": "./example-deno"
  },
  "dependencies": {
    "deno-bin": "^1.39.0"
  }
}

This names the project and creates three scripts that you can use to manage the project:

  • dev: Runs the project in development mode, allowing access to the network and environment while watching the file for changes.
  • build: Compiles the project into a single binary file.
  • start: Runs the file compiled by the build script.

You can run these with deno task <SCRIPT> or npm run <SCRIPT> if you have npm installed locally.

Deno knows how to use package.json files natively now, so we can rely on that file for our script definitions for more flexibility. If you know that you don't need compatibility with the Node.js ecosystem, you could alternatively define a deno.json file with just the script definitions (called tasks in this context) instead:

deno.json
{
  "tasks": {
    "dev": "deno run --watch --allow-net --allow-env main.ts",
    "build": "deno compile --allow-net --allow-env --output=example-deno main.ts",
    "start": "./example-deno"
  }
}

This file defines the same scripts as the package.json file without the deno-bin compatibility tool. This is helpful if you plan on using Deno-native files and plan on using Docker to build your project.

Create a Dockerfile for the project (Optional)

We can build and run our Deno project on Koyeb using the Node.js buildpack with the package.json file defined above. However, we can also optionally build from a Dockerfile for more control. To make it possible to build a container image for our application, we just need to create the Dockerfile. We'll also define a .dockerignore file to tell the builder what files to skip when creating the image.

Start by defining the .dockerignore file in your main project directory. Inside, paste the following contents:

.dockerignore
.git
.gitignore
Dockerfile
.dockerignore
node_modules
tmp
.env
example-deno
README.md

This file tells Docker to not include Git files, the Docker files themselves, dependency directories, built artifacts, or environment files unless explicitly called out within the Dockerfile. This helps ensure that the image we build is not bloated and that the build completes faster.

Next, create a new file called Dockerfile within the main project directory with the following contents:

Dockerfile
FROM denoland/deno
 
WORKDIR /app
 
COPY main.ts /app
RUN deno cache main.ts
 
ARG PORT
EXPOSE ${PORT:-8000}
 
CMD ["run", "--allow-net", "--allow-env", "main.ts"]

This Dockerfile uses the official Deno image (opens in a new tab) as a base. It copies the application file to the image and uses the deno cache to resolve and cache any dependencies. Afterwards, it runs the application as normal.

If you have Docker installed locally, you can build and test the image on your computer and optionally upload it to a registry. You can deploy container images from any container registry to Koyeb.

We can also build the Dockerfile directly from the repository when we deploy, which is useful as a way of automatically deploying when changes occur. We will demonstrate this method as one of the options in this guide.

Push the project to GitHub

Once you're finished configuring the project locally, you can commit your work and push it to GitHub.

Start by downloading the generic .gitignore file for Node.js projects from GitHub. We can use this as a starting point for our project:

curl -L https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore -o .gitignore

Add an entry for the artifact produced by the build task that we configured:

echo "example-deno" >> .gitignore

Run the following commands to create a new Git repository within the project's root directory, commit the project files, and push changes to GitHub.

Remember to replace the values of <YOUR_GITHUB_USERNAME> and <YOUR_REPOSITORY_NAME> with your own information:

git init
git add :/
git commit -m "Add project files"
git remote add origin git@github.com:<YOUR_GITHUB_USERNAME>/<YOUR_REPOSITORY_NAME>.git
git push -u origin main

Deploy to Koyeb using git-driven deployment

Once the repository is pushed to GitHub, you can deploy the Deno application to Koyeb. Any changes in the deployed branch of your codebase will automatically trigger a redeploy on Koyeb, ensuring that your application is always up-to-date.

To deploy the Deno app on Koyeb using the control panel (opens in a new tab), follow the steps below:

  1. Click Create Web Service on the Overview tab of the Koyeb control panel.
  2. Select GitHub as the deployment option.
  3. Choose the GitHub repository and branch containing your application code. Alternatively, you can enter our public Deno example repository (opens in a new tab) into the Public GitHub repository at the bottom of the page: https://github.com/koyeb/example-deno
  4. Choose the Builder for your project. We can use either a Dockerfile or buildpack for this repository.
  5. Name your Service, for example deno-service.
  6. Name the App, for example example-deno.
  7. Click the Deploy button.

A Koyeb App and Service will be created. Your application will be built and deployed to Koyeb. Once the build has finished, you will be able to access your application running on Koyeb by clicking the URL ending with .koyeb.app.

Deploy to Koyeb from a container registry

If you chose to build a container image for the Deno application, you can optionally deploy the application from a container registry instead of from GitHub.

To deploy a pre-built Deno container image on Koyeb using the control panel (opens in a new tab), follow the steps below:

  1. Click Create Web Service on the Overview tab of the Koyeb control panel.
  2. Select Docker as the deployment option.
  3. Choose the container image and tag from your registry and click Next to continue.
  4. Name your Service, for example deno-service.
  5. Name the App, for example example-deno.
  6. Click the Deploy button.

A Koyeb App and Service will be created. The container image will be pulled from the container registry and a container will be deployed from it. Once the initialization is complete, you will be able to access your application running on Koyeb by clicking the URL ending with .koyeb.app.