Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 19 additions & 9 deletions runtime/reference/docker.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,24 @@ To use the official image, create a `Dockerfile` in your project directory:
```dockerfile
FROM denoland/deno:latest

# Create working directory
WORKDIR /app

# Copy source
COPY . .
# Copy manifests first so the dependency install layer caches across
# source-only edits
COPY deno.json deno.lock package.json* ./
RUN deno ci --prod --skip-types

# Install dependencies (use just `deno install` if deno.json has imports)
RUN deno install --entrypoint main.ts
# Then copy the rest of the source
COPY . .

# Run the app
CMD ["deno", "run", "--allow-net", "main.ts"]
```

[`deno ci`](/runtime/reference/cli/ci/) performs a reproducible install from
`deno.lock`. `--prod` skips `devDependencies`, and `--skip-types` drops
`@types/*` packages — both shrink the resulting image without affecting
runtime behavior.

### Best Practices

#### Use Multi-stage Builds
Expand All @@ -39,9 +44,14 @@ FROM denoland/deno:latest AS builder
# Point Deno's cache at a known location so it can be copied to the next stage
ENV DENO_DIR=/deno-dir
WORKDIR /app

# Copy manifests first so the dependency install layer caches across
# source-only edits
COPY deno.json deno.lock package.json* ./
RUN deno ci --prod --skip-types

# Then copy the rest of the source
COPY . .
# Install dependencies (use just `deno install` if deno.json has imports)
RUN deno install --entrypoint main.ts

# Production stage
FROM denoland/deno:latest
Expand All @@ -53,7 +63,7 @@ COPY --from=builder /deno-dir /deno-dir
CMD ["deno", "run", "--allow-net", "main.ts"]
```

Without copying `$DENO_DIR`, `deno install` only writes to Deno's global cache
Without copying `$DENO_DIR`, `deno ci` only writes to Deno's global cache
inside the builder stage — those files do not travel with
`COPY --from=builder /app .`, so the container re-downloads dependencies on
first run.
Expand Down