Skip to content
Open
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
66 changes: 65 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,75 @@ WORKDIR /src
COPY [".", "./"]
RUN dotnet build "./src/Service/Azure.DataApiBuilder.Service.csproj" -c Docker -o /out -r linux-x64

FROM mcr.microsoft.com/dotnet/aspnet:10.0-azurelinux3.0 AS runtime
# ---------------------------------------------------------------------------
# Common runtime base.
#
# Not intended as a final build target. Both the `runtime` (root, default)
# and `runtime-nonroot` (non-root, scanner-friendly) variants derive from
# this stage so the shared setup stays in one place.
# ---------------------------------------------------------------------------
FROM mcr.microsoft.com/dotnet/aspnet:10.0-azurelinux3.0 AS runtime-base

COPY --from=build /out /App
# Add default dab-config.json to /App in the image
COPY --from=build /out/dab-config.json /App/dab-config.json
WORKDIR /App
ENV ASPNETCORE_URLS=http://+:5000
EXPOSE 5000
ENTRYPOINT ["dotnet", "Azure.DataApiBuilder.Service.dll"]

# ---------------------------------------------------------------------------
# Non-root variant. Build explicitly with:
# docker build --target runtime-nonroot -t <repo>:<version>-nonroot .
#
# Runs as the "app" user that ships with the mcr.microsoft.com/dotnet/aspnet
# base image (UID/GID 1654, exposed via the APP_UID env var, on the
# azurelinux3.0 variant). DAB does not require root, and declaring USER
# explicitly sets the image's Config.User field so image scanners
# (e.g. Checkmarx One) that require a non-root user in the final stage are
# satisfied.
#
# Safeguards applied to minimize the chance of runtime breakage:
# * Pre-create /App/logs and `chown app:app /App/logs` (non-recursive) so
# the documented default file-sink path ("logs/dab-log.txt", relative to
# WORKDIR /App) is writable. Ownership of the published assemblies under
# /App is intentionally left unchanged - a recursive chown would
# duplicate every assembly layer and roughly double the image size for

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment here is contradictory to the description in the PR which says that we are doing: chown -R app:app /App recursive.

However, not doing recursive seems the right thing to do. The PR description should be fixed.

# no runtime benefit, since DAB only needs write access to /App/logs.
# * Default port stays at 5000, which is above 1024, so binding works
# without CAP_NET_BIND_SERVICE. Users overriding ASPNETCORE_URLS to a
# privileged port (<1024) must add `--cap-add=NET_BIND_SERVICE` to
# `docker run` or front DAB with a reverse proxy.
#
# Notes for consumers of this image:
# * Host bind-mounts (config, logs, certs, etc.) must be readable - and
# writable, if DAB needs to write them - by UID 1654 on the host.
# Either `chown -R 1654:1654 /host/path` or, in Kubernetes, set
# `securityContext.fsGroup: 1654`.
# * `docker exec` defaults to the `app` user. Use `docker exec --user 0`
# for administrative actions inside a running container.
# * Downstream Dockerfiles (FROM <this image>) that need to install
# packages or write outside /App should add `USER 0` before those
# instructions, then restore `USER app` at the end.
# ---------------------------------------------------------------------------
FROM runtime-base AS runtime-nonroot

RUN mkdir -p /App/logs && chown app:app /App/logs
USER app

LABEL org.opencontainers.image.title="Data API builder (non-root)" \
org.opencontainers.image.description="Data API builder running as the non-root 'app' user (UID 1654 on azurelinux3.0)." \
org.opencontainers.image.source="https://github.com/Azure/data-api-builder"

# ---------------------------------------------------------------------------
# Default (root-running) variant. Build with either:
# docker build -t <repo>:<version> . # no --target needed
# docker build --target runtime -t <repo>:<version> .
#
# This is the LAST stage in the file, so a plain `docker build` with no
# --target argument produces this image. Keeping the root-running variant
# as the default preserves backwards compatibility with the previously
# published image - existing users see no behavior change. The non-root
# variant is opt-in via `--target runtime-nonroot`.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was the requirement for this PR approved by any PM in any of our scrum syncs?

# ---------------------------------------------------------------------------
FROM runtime-base AS runtime

@Aniruddh25 Aniruddh25 Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Followup separate PR in internal repo would need changes to both the pipeline yml as well as the docker file used in one branch. The Dockerfile that is changed here is only shown as a sample for building the image by customers

@Aniruddh25 Aniruddh25 Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the FROM statement as runtime, is the last in the Dockerfile, would the ENTRYPOINT mentioned on line 25 still stay the same?

Did you actually test this to build and start dab in the container?