If you're just here to copy and paste, here's the final Dockerfile that will produce an image for your Deno app:
Dockerfile
FROM denoland/deno:2.2.1
# The port that your application listens to.
EXPOSE 1993
WORKDIR /app
# Prefer not to run as root.
USER deno
# Cache the dependencies as a layer (the following two steps are re-run only when deps.ts is modified).
# Ideally cache deps.ts will download and compile _all_ external files used in main.ts.
COPY deps.ts .
RUN deno install --entrypoint deps.ts
# These steps will be re-run upon each file change in your working directory:
COPY . .
# Compile the main app so that it doesn't need to be compiled each startup/entry.
RUN deno cache main.ts
CMD ["run", "--allow-net", "main.ts"]
And here's the .dockerignore
file:
.DS_Store
To build and run the image, use these commands:
docker build -t deno-app .
docker run -p 1993:1993 deno-app
Not just here to copy and paste? Let's go over what's happening in the Dockerfile!
The Setup
For this tutorial, I assume you have a basic Deno project set up—likely with a main.ts
file as your entry point and a deps.ts
file for managing dependencies (a common Deno convention). If your setup differs, you might need to tweak the Dockerfile accordingly.
Typically, you'd run deno run --allow-net main.ts
to work locally. For deployment, though, we want to package everything into a Docker container that's lightweight, secure, and optimized. Let's break down the Dockerfile step by step.
The Dockerfile Explained
Let's examine what each part does:
Base image
- Uses
denoland/deno:2.2.1
, the official Deno image, which comes with Deno pre-installed and ready to go.
Port exposure
- Exposes port 1993, the default port for this example. Adjust this if your app listens on a different port. Keep in mind that
EXPOSE
doesn't actually expose the port to the outside world. It's just for documentation purposes.
Working directory
- Sets
/app
as the working directory inside the container.
Security
- Switches to the
deno
user (non-root) for better security—running as root in containers is a no-no unless absolutely necessary.
Dependency caching
- Copies only
deps.ts
first and runsdeno install --entrypoint deps.ts
to cache dependencies. - This step ensures that dependencies are only re-downloaded or re-compiled if
deps.ts
changes, speeding up builds.
App setup
- Copies the rest of your project files (like
main.ts
) into the container. - Runs
deno cache main.ts
to pre-compile the app, reducing startup time when the container runs.
Runtime
- Starts the app with
deno run --allow-net main.ts
, granting network access (common for web servers). - Add more permissions (e.g.,
--allow-env
,--allow-read
) if your app needs them.
This Dockerfile keeps things simple and lean, leveraging Deno's built-in features like dependency management and compilation. Add a .dockerignore
file to skip unnecessary files like .DS_Store
, though Deno projects typically don't have a node_modules
folder to ignore.
Deployment
Sliplane is a fully managed container hosting solution that simplifies Docker deployment and management. It offers:
- Automatic building and deployment of containers from GitHub repositories
- Continuous deployment on every push
- Unlimited container hosting on each server for a fixed monthly fee
- Built-in features like domains, vertical scaling, SSL, and security updates
I think the best part about sliplane.io is that you can deploy as many containers as you want without paying extra, allowing you to try out new ideas without the fear of breaking the bank (looking at you, AWS). To get started, simply connect your Github repository with your Deno app and deploy in a single click!
Conclusion
Is there anything else you'd like to know? Need help dockerizing your Deno app further? Want assistance deploying to a specific platform? Feel free to reach out! You can find me on X or just drop a comment below.
Cheers,
Jonas
Top comments (0)