Skip to content

Commit 6d1ccc8

Browse files
docs: Add a section in the Rust guide to use DHI (#23728)
<!--Delete sections as needed --> ## Description Updated the Rust language guide to include Docker Hardened Images (DHI). Changes include: - Added DHI as an option with tab-based layout (Official image / Hardened image) - Added a DHI-based Dockerfile example - Updated build instructions to match docker init behavior - Added DHI quickstart link in "Related information" This follows the same structure used in the [Bun](#23565) and [Ruby](#23691) DHI updates. ## Reviews <!-- Notes for reviewers here --> <!-- List applicable reviews (optionally @tag reviewers) --> - [ ] Technical review - [x] Editorial review - [ ] Product review --------- Co-authored-by: Craig Osterhout <craig.osterhout@docker.com>
1 parent ca0e85c commit 6d1ccc8

2 files changed

Lines changed: 230 additions & 72 deletions

File tree

content/guides/rust/build-images.md

Lines changed: 203 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ This utility will walk you through creating the following files with sensible de
5050
Let's get started!
5151

5252
? What application platform does your project use? Rust
53-
? What version of Rust do you want to use? 1.70.0
53+
? What version of Rust do you want to use? 1.92.0
5454
? What port does your server listen on? 8000
5555
```
5656

@@ -62,6 +62,165 @@ directory:
6262
- compose.yaml
6363
- README.Docker.md
6464

65+
## Choose a base image
66+
67+
Before editing your Dockerfile, you need to choose a base image. You can use the [Rust Docker Official Image](https://hub.docker.com/_/rust),
68+
or a [Docker Hardened Image (DHI)](https://hub.docker.com/hardened-images/catalog/dhi/rust).
69+
70+
Docker Hardened Images (DHIs) are minimal, secure, and production-ready base images maintained by Docker.
71+
They help reduce vulnerabilities and simplify compliance. For more details, see [Docker Hardened Images](/dhi/).
72+
73+
{{< tabs >}}
74+
{{< tab name="Using Docker Hardened Images" >}}
75+
76+
Docker Hardened Images (DHIs) are publicly available and can be used directly as base images.
77+
To pull Docker Hardened Images, authenticate once with Docker:
78+
79+
```bash
80+
docker login dhi.io
81+
```
82+
83+
Use DHIs from the dhi.io registry, for example:
84+
85+
```bash
86+
FROM dhi.io/rust:${RUST_VERSION}-alpine3.22-dev AS build
87+
```
88+
89+
The following Dockerfile is equivalent to the one generated by `docker init`, but it uses a Rust DHI as the build base image:
90+
91+
```dockerfile {title=Dockerfile}
92+
# Make sure RUST_VERSION matches the Rust version
93+
ARG RUST_VERSION=1.92
94+
ARG APP_NAME=docker-rust-hello
95+
96+
################################################################################
97+
# Create a stage for building the application.
98+
################################################################################
99+
100+
FROM dhi.io/rust:${RUST_VERSION}-alpine3.22-dev AS build
101+
ARG APP_NAME
102+
WORKDIR /app
103+
104+
# Install host build dependencies.
105+
RUN apk add --no-cache clang lld musl-dev git
106+
107+
# Build the application.
108+
RUN --mount=type=bind,source=src,target=src \
109+
--mount=type=bind,source=Cargo.toml,target=Cargo.toml \
110+
--mount=type=bind,source=Cargo.lock,target=Cargo.lock \
111+
--mount=type=cache,target=/app/target/ \
112+
--mount=type=cache,target=/usr/local/cargo/git/db \
113+
--mount=type=cache,target=/usr/local/cargo/registry/ \
114+
cargo build --locked --release && \
115+
cp ./target/release/$APP_NAME /bin/server
116+
117+
################################################################################
118+
# Create a new stage for running the application that contains the minimal
119+
# We use dhi.io/static for the final stage because it’s a minimal Docker Hardened Image runtime (basically “just # enough OS to run the binary”), which helps keep the image small and with a lower attack surface compared to a # # full Alpine/Debian runtime.
120+
################################################################################
121+
122+
FROM dhi.io/static:20250419 AS final
123+
124+
# Create a non-privileged user that the app will run under.
125+
ARG UID=10001
126+
RUN adduser \
127+
--disabled-password \
128+
--gecos "" \
129+
--home "/nonexistent" \
130+
--shell "/sbin/nologin" \
131+
--no-create-home \
132+
--uid "${UID}" \
133+
appuser
134+
USER appuser
135+
136+
# Copy the executable from the "build" stage.
137+
COPY --from=build /bin/server /bin/
138+
139+
# Configure rocket to listen on all interfaces.
140+
ENV ROCKET_ADDRESS=0.0.0.0
141+
142+
# Expose the port that the application listens on.
143+
EXPOSE 8000
144+
145+
# What the container should run when it is started.
146+
CMD ["/bin/server"]
147+
148+
```
149+
150+
{{< /tab >}}
151+
{{< tab name="Using the Docker Official Images" >}}
152+
153+
```dockerfile {title=Dockerfile}
154+
# Pin the Rust toolchain version used in the build stage.
155+
ARG RUST_VERSION=1.92
156+
157+
# Name of the compiled binary produced by Cargo (must match Cargo.toml package name).
158+
ARG APP_NAME=docker-rust-hello
159+
160+
################################################################################
161+
# Build stage (DOI Rust image)
162+
# This stage compiles the application.
163+
################################################################################
164+
165+
FROM docker.io/library/rust:${RUST_VERSION}-alpine AS build
166+
167+
# Re-declare args inside the stage if you want to use them here.
168+
ARG APP_NAME
169+
170+
# All build steps happen inside /app.
171+
WORKDIR /app
172+
173+
# Install build dependencies needed to compile Rust crates on Alpine
174+
RUN apk add --no-cache clang lld musl-dev git
175+
176+
# Build the application
177+
RUN --mount=type=bind,source=src,target=src \
178+
--mount=type=bind,source=Cargo.toml,target=Cargo.toml \
179+
--mount=type=bind,source=Cargo.lock,target=Cargo.lock \
180+
--mount=type=cache,target=/app/target/ \
181+
--mount=type=cache,target=/usr/local/cargo/git/db \
182+
--mount=type=cache,target=/usr/local/cargo/registry/ \
183+
cargo build --locked --release && \
184+
cp ./target/release/$APP_NAME /bin/server
185+
186+
################################################################################
187+
# Runtime stage (DOI Alpine image)
188+
# This stage runs the already-compiled binary with minimal dependencies.
189+
################################################################################
190+
191+
FROM docker.io/library/alpine:3.18 AS final
192+
193+
# Create a non-privileged user (recommended best practice)
194+
ARG UID=10001
195+
RUN adduser \
196+
--disabled-password \
197+
--gecos "" \
198+
--home "/nonexistent" \
199+
--shell "/sbin/nologin" \
200+
--no-create-home \
201+
--uid "${UID}" \
202+
appuser
203+
204+
# Drop privileges for runtime.
205+
USER appuser
206+
207+
# Copy only the compiled binary from the build stage.
208+
COPY --from=build /bin/server /bin/
209+
210+
# Rocket: listen on all interfaces inside the container.
211+
ENV ROCKET_ADDRESS=0.0.0.0
212+
213+
# Document the port your app listens on.
214+
EXPOSE 8000
215+
216+
# Start the application.
217+
CMD ["/bin/server"]
218+
```
219+
{{< /tab >}}
220+
{{< /tabs >}}
221+
222+
223+
65224
For building an image, only the Dockerfile is necessary. Open the Dockerfile
66225
in your favorite IDE or text editor and see what it contains. To learn more
67226
about Dockerfiles, see the [Dockerfile reference](/reference/dockerfile.md).
@@ -85,33 +244,40 @@ Docker uses "latest" as its default tag.
85244
Build the Docker image.
86245

87246
```console
88-
$ docker build --tag docker-rust-image .
247+
$ docker build --tag docker-rust-image-dhi .
89248
```
90249

91250
You should see output like the following.
92251

93252
```console
94-
[+] Building 62.6s (14/14) FINISHED
95-
=> [internal] load .dockerignore 0.1s
96-
=> => transferring context: 2B 0.0s
97-
=> [internal] load build definition from Dockerfile 0.1s
98-
=> => transferring dockerfile: 2.70kB 0.0s
99-
=> resolve image config for docker.io/docker/dockerfile:1 2.3s
100-
=> CACHED docker-image://docker.io/docker/dockerfile:1@sha256:39b85bbfa7536a5feceb7372a0817649ecb2724562a38360f4d6a7782a409b14 0.0s
101-
=> [internal] load metadata for docker.io/library/debian:bullseye-slim 1.9s
102-
=> [internal] load metadata for docker.io/library/rust:1.70.0-slim-bullseye 1.7s
103-
=> [build 1/3] FROM docker.io/library/rust:1.70.0-slim-bullseye@sha256:585eeddab1ec712dade54381e115f676bba239b1c79198832ddda397c1f 0.0s
104-
=> [internal] load build context 0.0s
105-
=> => transferring context: 35.29kB 0.0s
106-
=> [final 1/3] FROM docker.io/library/debian:bullseye-slim@sha256:7606bef5684b393434f06a50a3d1a09808fee5a0240d37da5d181b1b121e7637 0.0s
107-
=> CACHED [build 2/3] WORKDIR /app 0.0s
108-
=> [build 3/3] RUN --mount=type=bind,source=src,target=src --mount=type=bind,source=Cargo.toml,target=Cargo.toml --mount= 57.7s
109-
=> CACHED [final 2/3] RUN adduser --disabled-password --gecos "" --home "/nonexistent" --shell "/sbin/nologin" 0.0s
110-
=> CACHED [final 3/3] COPY --from=build /bin/server /bin/ 0.0s
111-
=> exporting to image 0.0s
112-
=> => exporting layers 0.0s
113-
=> => writing image sha256:f1aa4a9f58d2ecf73b0c2b7f28a6646d9849b32c3921e42adc3ab75e12a3de14 0.0s
114-
=> => naming to docker.io/library/docker-rust-image
253+
[+] Building 1.4s (13/13) FINISHED docker:desktop-linux
254+
=> [internal] load build definition from Dockerfile 0.0s
255+
=> => transferring dockerfile: 1.67kB 0.0s
256+
=> [internal] load metadata for dhi.io/static:20250419 1.1s
257+
=> [internal] load metadata for dhi.io/rust:1.92-alpine3.22-dev 1.2s
258+
=> [auth] static:pull token for dhi.io 0.0s
259+
=> [auth] rust:pull token for dhi.io 0.0s
260+
=> [internal] load .dockerignore 0.0s
261+
=> => transferring context: 646B 0.0s
262+
=> [build 1/3] FROM dhi.io/rust:1.92-alpine3.22-dev@sha256:49eb72825a9e15fe48f2c4875a63c7e7f52a5b430bb52b8254b91d132aa5bf38 0.0s
263+
=> => resolve dhi.io/rust:1.92-alpine3.22-dev@sha256:49eb72825a9e15fe48f2c4875a63c7e7f52a5b430bb52b8254b91d132aa5bf38 0.0s
264+
=> [final 1/2] FROM dhi.io/static:20250419@sha256:74fc43fa240887b8159970e434244039aab0c6efaaa9cf044004cdc22aa2a34d 0.0s
265+
=> => resolve dhi.io/static:20250419@sha256:74fc43fa240887b8159970e434244039aab0c6efaaa9cf044004cdc22aa2a34d 0.0s
266+
=> [internal] load build context 0.0s
267+
=> => transferring context: 117B 0.0s
268+
=> CACHED [build 2/3] WORKDIR /build 0.0s
269+
=> CACHED [build 3/3] RUN --mount=type=bind,source=src,target=src --mount=type=bind,source=Cargo.toml,target=Cargo.toml --mount=type=bind,source=Cargo.lock,target=Cargo 0.0s
270+
=> CACHED [final 2/2] COPY --from=build /build/target/release/docker-rust-hello /server 0.0s
271+
=> exporting to image 0.1s
272+
=> => exporting layers 0.0s
273+
=> => exporting manifest sha256:cc937bbdd712ef6e5445501f77e02ef8455ef64c567598786d46b7b21a4d4fa8 0.0s
274+
=> => exporting config sha256:077507b483af4b5e1a928e527e4bb3a4aaf0557e1eea81cd39465f564c187669 0.0s
275+
=> => exporting attestation manifest sha256:11b60e7608170493da1fdd88c120e2d2957f2a72a22edbc9cfbdd0dd37d21f89 0.0s
276+
=> => exporting manifest list sha256:99a1b925a8d6ebf80e376b8a1e50cd806ec42d194479a3375e1cd9d2911b4db9 0.0s
277+
=> => naming to docker.io/library/docker-rust-image-dhi:latest 0.0s
278+
=> => unpacking to docker.io/library/docker-rust-image-dhi:latest 0.0s
279+
280+
View build details: docker-desktop://dashboard/build/desktop-linux/desktop-linux/yczk0ijw8kc5g20e8nbc8r6lj
115281
```
116282

117283
## View local images
@@ -122,11 +288,11 @@ To list images, run the `docker images` command.
122288

123289
```console
124290
$ docker images
125-
REPOSITORY TAG IMAGE ID CREATED SIZE
126-
docker-rust-image latest 8cae92a8fbd6 3 minutes ago 123MB
291+
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
292+
docker-rust-image-dhi:latest 99a1b925a8d6 11.6MB 2.45MB U
127293
```
128294

129-
You should see at least one image listed, including the image you just built `docker-rust-image:latest`.
295+
You should see at least one image listed, including the image you just built `docker-rust-image-dhi:latest`.
130296

131297
## Tag images
132298

@@ -137,7 +303,7 @@ An image is made up of a manifest and a list of layers. Don't worry too much abo
137303
To create a new tag for the image you built, run the following command.
138304

139305
```console
140-
$ docker tag docker-rust-image:latest docker-rust-image:v1.0.0
306+
$ docker tag docker-rust-image-dhi:latest docker-rust-image-dhi:v1.0.0
141307
```
142308

143309
The `docker tag` command creates a new tag for an image. It doesn't create a new image. The tag points to the same image and is just another way to reference the image.
@@ -146,31 +312,29 @@ Now, run the `docker images` command to see a list of the local images.
146312

147313
```console
148314
$ docker images
149-
REPOSITORY TAG IMAGE ID CREATED SIZE
150-
docker-rust-image latest 8cae92a8fbd6 4 minutes ago 123MB
151-
docker-rust-image v1.0.0 8cae92a8fbd6 4 minutes ago 123MB
152-
rust latest be5d294735c6 4 minutes ago 113MB
315+
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
316+
docker-rust-image-dhi:latest 99a1b925a8d6 11.6MB 2.45MB U
317+
docker-rust-image-dhi:v1.0.0 99a1b925a8d6 11.6MB 2.45MB U
153318
```
154319

155-
You can see that two images start with `docker-rust-image`. You know they're the same image because if you take a look at the `IMAGE ID` column, you can see that the values are the same for the two images.
320+
You can see that two images start with `docker-rust-image-dhi`. You know they're the same image because if you take a look at the `IMAGE ID` column, you can see that the values are the same for the two images.
156321

157322
Remove the tag you just created. To do this, use the `rmi` command. The `rmi` command stands for remove image.
158323

159324
```console
160-
$ docker rmi docker-rust-image:v1.0.0
161-
Untagged: docker-rust-image:v1.0.0
325+
$ docker rmi docker-rust-image-dhi:v1.0.0
326+
Untagged: docker-rust-image-dhi:v1.0.0
162327
```
163328

164329
Note that the response from Docker tells you that Docker didn't remove the image, but only "untagged" it. You can check this by running the `docker images` command.
165330

166331
```console
167332
$ docker images
168-
REPOSITORY TAG IMAGE ID CREATED SIZE
169-
docker-rust-image latest 8cae92a8fbd6 6 minutes ago 123MB
170-
rust latest be5d294735c6 6 minutes ago 113MB
333+
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
334+
docker-rust-image-dhi:latest 99a1b925a8d6 11.6MB 2.45MB U
171335
```
172336

173-
Docker removed the image tagged with `:v1.0.0`, but the `docker-rust-image:latest` tag is available on your machine.
337+
Docker removed the image tagged with `:v1.0.0`, but the `docker-rust-image-dhi:latest` tag is available on your machine.
174338

175339
## Summary
176340

@@ -182,6 +346,7 @@ Related information:
182346
- [.dockerignore file](/reference/dockerfile.md#dockerignore-file)
183347
- [docker init CLI reference](/reference/cli/docker/init.md)
184348
- [docker build CLI reference](/reference/cli/docker/buildx/build.md)
349+
- [Docker Hardened Images](/dhi/)
185350

186351
## Next steps
187352

0 commit comments

Comments
 (0)