deno-deploy
Preparing for deployment
Including gitignored files
The .gitignore is taken into account for the deno publish command. In Deno 1.41.2+, you can opt-out of excluded files ignored in the .gitignore by using a negated exclude glob:
dist/
.env
And in the deno.json:
{
"publish": {
"exclude": [
// include the .gitignored dist folder
"!dist/"
]
}
}
Alternatively, explicitly specifying the gitignored paths in an "include" works as well:
{
"publish": {
"include": [
"dist/",
"README.md",
"deno.json"
]
}
}
Databases
Once you've assigned a database to your app, connecting to it from your code is simple. Deno Deploy automatically handles connection details, credentials, and environment variables for you, meaning deno handles the switch between production, git branch, and development databases all via injecting different database URLs for the db connection env var for different environments.
There are two ways to add a database instance to your Deno Deploy organization:
- Link Database: Connect an existing external database instance (for example, a PostgreSQL server you run or a managed instance from a cloud provider).
- Provision Database: Create and attach a managed data store from Deno Deploy (Deno KV or Prisma Postgres).
Deno KV
For Deno KV, you can use the built-in Deno.openKv() API to connect to your assigned Deno KV instance. No additional configuration is needed - Deno Deploy automatically connects your app to the correct Deno KV instance based on the current environment.
// No arguments needed - Deno Deploy handles this automatically
const kv = await Deno.openKv();
Deno.serve(async () => {
// Use the Deno KV instance
await kv.set(["user", "123"], { name: "Alice", age: 30 });
const user = await kv.get(["user", "123"]);
return new Response(JSON.stringify(user.value), {
headers: { "content-type": "application/json" },
});
});
PostgreSQL
For PostgreSQL databases (both external and provisioned), Deno Deploy automatically injects standard database environment variables into your app's runtime environment:
DATABASE_URL: A full connection string for the current environment, in the formatpostgresql://username:password@hostname:port/database.PGHOST: The database server hostname.PGPORT: The database server port.PGDATABASE: The database name for the current environment.PGUSER: The database username.PGPASSWORD: The database password.
If your database requires a custom SSL/TLS certificate, Deno Deploy also injects that certificate into the default certificate store, so that all SSL connections work automatically.
You can use your favorite PostgreSQL client library (such as pg from npm) to connect to your database using these environment variables. Most libraries automatically detect and use these standard environment variables without any configuration.
As an example, here's how to connect to PostgreSQL in your Deno Deploy app:
import { Pool } from "npm:pg";
// No arguments needed - the library reads connection details from environment variables automatically
const pool = new Pool();
Deno.serve(async () => {
// Use the database
const result = await pool.query("SELECT * FROM users WHERE id = $1", [123]);
return new Response(JSON.stringify(result.rows), {
headers: { "content-type": "application/json" },
});
});
Running migrations
Since each environment has its own isolated database, you will often have many separate databases to manage in every app. It is not practical to manually run migrations or insert seed data into each database individually every time you deploy a new revision.
To streamline this process, Deno Deploy allows you to configure an automated pre-deploy command that runs every time a revision is rolled out to a timeline, before the deployment starts serving traffic.
To set up an automated migration command, head to the Settings page of your app, and go to the App Config section. There you can edit the app configuration to set a pre-deploy command in the "Pre-Deploy Command" field (for example, deno task migrate or npm run migrate).
You can see the detailed logs of the pre-deploy command execution in the revision build logs, in the "Deployment" section.
As an example, you could set up a migration script using node-pg-migrate:
-
Add a task to your
deno.json:{
"tasks": {
"migrate": "deno run --allow-net --allow-env --allow-read --allow-write npm:node-pg-migrate up"
}
} -
Create a migrations directory and add migration files. For example,
migrations/1234567890_create-users-table.js:exports.up = (pgm) => {
pgm.createTable("users", {
id: "id",
name: { type: "varchar(100)", notNull: true },
email: { type: "varchar(100)", notNull: true },
created_at: {
type: "timestamp",
notNull: true,
default: pgm.func("current_timestamp"),
},
});
};
exports.down = (pgm) => {
pgm.dropTable("users");
}; -
Set your pre-deploy command to
deno task migratein the app settings.
Deno Deploy will automatically run this command before each deployment, ensuring all your environment-specific databases stay up to date.
Environment Variables
Contexts
Each environment variable applies to one or more contexts. Contexts represent the logical "environments" in which your code runs, each with its own set of variables and secrets.
By default, there are two contexts:
- Production: Used for the production timeline serving production traffic
- Development: Used for development timelines serving non-production traffic (preview URLs and branch URLs)
Env vars in all contexts
Deno Deploy provides these predefined environment variables in all contexts:
DENO_DEPLOY:true- Indicates that the code is running in Deno Deploy.DENO_DEPLOY_ORG_ID: The ID of the organization that owns the application.- This is a UUID.
DENO_DEPLOY_ORG_SLUG: The slug of the organization that owns the application. This is the human-readable identifier used in URLs that was set when creating the organization.DENO_DEPLOY_APP_ID: The ID of the application. This is a UUID.DENO_DEPLOY_APP_SLUG: The slug of the application. This is the human-readable identifier used in URLs that was set when creating the application, or changed later in the application settings.DENO_DEPLOY_BUILD_ID: The ID of the currently running build.DENO_DEPLOYMENT_ID: A unique identifier representing the entire configuration set (application ID, build ID, context, environment variables, cloud connections, database). Changes if any of these components change.DENO_DEPLOY_BUILD_ID: The ID of the currently running revision.DENO_TIMELINE: The timeline the application is currently running in. Possible values areproduction,git-branch/<branch-name>, andpreview/<revision-id>. This is not set during builds, as builds are not specific to any timeline.
During builds, the environment variable CI=1 is additionally set.
deployctl
To use deno deploy, you can install the deployctl tool first off:
deno install -gArf jsr:@deno/deployctl
Then deploying a deno app is as easy as one command:
deployctl deploy
After running the deploy command, deno will add build configuration settings to the deno.json. There are also command line options that will be useful here:
--env-file: Points to the env file to load for deployment.--env-file=.envwill load all env variables from the.envfile into the deployment build.--include: file and folders in glob path syntax to include--entrypoint: the main deno file to run. This will often be just a server listening initialization.
Here is an example deno deploy command that works for fullstack deno projects
{
"tasks": {
"dev": "deno run -A --env-file=.env.local --watch main.ts",
"build:node": "cd frontend && npm run build", // build frontend
"start": "deno run build:node && deno run -A --env-file=.env.local main.ts",
"deploy": "deno run build:node && deployctl deploy --env-file=.env.local --entrypoint=main.ts"
},
"imports": {
"@std/assert": "jsr:@std/assert@1"
},
"deploy": {
"project": "a226342a-e14a-4684-8070-86ed781ce36e",
"exclude": [
"**/node_modules"
],
"include": [],
"entrypoint": "main.ts"
}
}
deno.json deploy config
All deno deploy config is stored under the "deploy" key in the deno.json
deploy.framework(required unlessdeploy.runtimeis set): The framework preset to use, such asnextjsorfresh. Setting this option automatically configures defaults for the framework. Available presets are listed in the framework integrations docs.deploy.install(optional): Shell command to install dependencies.deploy.build(optional): Shell command to build the project.deploy.predeploy(optional): Shell command to run after the build is complete but before deployment, typically for tasks like database migrations.deploy.runtime(required unlessdeploy.frameworkis set): Configuration for how the app serves traffic. The app can either be static or dynamic, as defined below:- For dynamic apps:
deploy.runtime.type: Must be set to"dynamic", or omitted (dynamic is the default).deploy.runtime.entrypoint: The JavaScript or TypeScript file to execute.deploy.runtime.args(optional): Command-line arguments to pass to the application.deploy.runtime.cwd(optional): The working directory for the application at runtime.deploy.runtime.memory_limit(optional): The maximum amount of memory the application can use at runtime. Defaults to 768 MB, can be increased to 4 GB on the Pro plan.
- For static apps:
deploy.runtime.type: Must be set to"static".deploy.runtime.cwd: Folder containing static assets (e.g.,dist,.output).deploy.runtime.spa(optional): Iftrue, servesindex.htmlfor paths that don't match static files instead of returning 404 errors.
- For apps using a framework preset:
deploy.runtime.memory_limit(optional): The maximum amount of memory the application can use at runtime. Defaults to 768 MB, can be increased to 4 GB on the Pro plan.
- For dynamic apps:
basic server config example
{
"deploy": {
"install": "npm install",
"build": "npm run build",
"predeploy": "deno run --allow-net --allow-env migrate.ts",
"runtime": {
"type": "dynamic",
"entrypoint": "./app/server.js",
"args": ["--port", "8080"],
"cwd": "./app"
}
}
}
basic static app config example
{
"deploy": {
"install": "npm install",
"build": "npm run build",
"runtime": {
"type": "static",
"cwd": "./public",
"spa": true
}
}
}
Seeing logs
You can see the logs of your deployment in the command line by running this command:
deployctl logs
Listing deployments
This command lists all the deployments of the project you have made so far.
deployctl deployments list
Dealing with projects
All deployments are based on automatically created projects, configured within your deno.json and created automatically if you choose if to deploy before doing any config steps.
deployctl projects create: creates a new projectdeployctl projects show: shows more info about a specific projectdeployctl projects list: lists all projects you owndeployctl projects rename: renames a projectdeployctl projects delete: deletes a project
Creating projects
Before deploying, you should create an empty project like so, which simplifies config.
deployctl projects create <project-name>
Showing projects
Run the deployctl projects show command to see more info on a specific project.
deployctl projects show <project-name>
Deleting projects
Run the deployctl projects delete command to delete a project by its name
deployctl projects delete <project-name>
Listing projects
Run the deployctl projects list command to list all the projects you own.
deployctl projects list
Renaming projects
You can rename a project with the deployctl projects rename command, running it from within the root of the project.
deployctl projects rename <new-name>
Development to deploy workflow
Tunneling
tunneling is a way to preview your local development server on a production URL, and with deno tunneling, you can preview it as close as possible to a production environment with a temporary deno deploy URL and using environment variables from deno deploy.
Here are the steps to enable tunneling for your app:
- Deploy your app to deno deploy at least once
- Run
deno run --tunnel devto run tunneling while in dev mode
github actions
on:
repository_dispatch:
types: [deno_deploy.build.routed] # Listen for successful builds
jobs:
notify:
runs-on: ubuntu-latest
steps:
- name: Test the preview_url
run: |
echo "The Deno Deploy app is available at ${{ github.event.client_payload.revision.preview_url }}"
curl -I ${{ github.event.client_payload.revision.preview_url }}