---
title: Deploy an app from a Dockerfile
description: Deploy a long-running service, such as a webhook, an API or a tool for agents, from source code with a Dockerfile.
url: https://agentlab.cresclab.com/docs/en/deploy/container-app
language: en
updated: 2026-10-06
---

# Deploy an app from a Dockerfile

Deploy a long-running service, such as a webhook, an API or a tool for agents, from source code with a Dockerfile.

## Before you begin

- A program that answers HTTP requests.
- A `Dockerfile` at the top of the project.

## 1. Write the Dockerfile

This Node.js example listens on `$PORT`, exposes exactly one port, and runs as a non-root user:

```dockerfile Dockerfile
FROM node:22-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
USER node
EXPOSE 8080
CMD ["node", "server.js"]
```

```js server.js
import { createServer } from "node:http";

const port = Number(process.env.PORT ?? 8080);

createServer((req, res) => {
  res.writeHead(200, { "content-type": "application/json" });
  res.end(JSON.stringify({ ok: true, path: req.url }));
}).listen(port);
```

The image must meet these rules. The [container runtime contract](https://agentlab.cresclab.com/docs/en/deploy/runtime-contract.md) has the full list.

- A `CMD` or `ENTRYPOINT` starts the server.
- The server answers HTTP on `$PORT`.
- The image is built for `linux/amd64`.
- The filesystem is read-only apart from `/tmp`.

## 2. Upload the source

On the app's **Deploy** tab, drop the source folder or its `.zip` on the upload area, then select **Upload and build**. The `Dockerfile` must be at the top. If you make the zip yourself, leave out `node_modules`, `.git` and `.env`:

```bash
zip -r ../app.zip . -x 'node_modules/*' '.git/*' '.env*'
```

The platform builds the image, scans it for vulnerabilities and secrets, and checks it against the runtime contract. Anything the platform adapted shows as a notice on the version page, for example "The image runs as root, so it runs as uid 10001."

> [!NOTE] Already have an image?
> Push it to your organization's image registry, then choose **An image** under **Deploy a new version from** to make a version from it.

## 3. Go live and check the runtime

An app's first version goes live on its own once it builds. Later versions go live when you select **Go live** on the **Deploy** tab.

The **Runtime** tab shows which version is serving and how many instances are ready.

> [!WARNING] The version never becomes ready
> The usual cause is the port: the server isn't listening on the port the platform gave it. See [Troubleshoot failed deployments](https://agentlab.cresclab.com/docs/en/deploy/troubleshooting.md#not-ready).

## 4. Add secrets

Enter API keys and other secret values in the app's **Settings**. Your code reads them as environment variables. Saved settings apply when you restart the service, and the old instances keep serving until the new ones are ready. See [App settings](https://agentlab.cresclab.com/docs/en/deploy/app-settings.md#secrets).

> [!WARNING] Keep secrets out of your source
> If the scan finds a key in the image, take it out of the source, set it as a secret of the app instead, and rotate the key.
