1. Concept: Build Secrets vs. Build Args
When building Docker images, you often need sensitive data (NuGet tokens, NPM keys, API credentials) to fetch dependencies.
-
Build Args (
ARG): These are stored in the image's history. Runningdocker history <image>will reveal these values to anyone with access to the image. Never use these for secrets. -
Docker Secrets (
--secret): These are temporarily mounted into a specificRUNcommand. They exist only in memory or as a temporary file during that specific layer's execution. They are never committed to the image layers.
2. The Universal Dockerfile Template
This template uses a multi-stage build, a non-root user for security, and the Secret Mount syntax.
# --- Stage 1: Build ---
FROM mcr.microsoft.com/dotnet/sdk:10.0-bookworm-slim AS build-env
WORKDIR /app
# Non-sensitive configuration via ARGs
ARG PACKAGE_SOURCE_URL
ARG SOURCE_NAME
ARG SOURCE_USER
# Copy project files for restoration
COPY *.slnx ./
COPY src/**/*.csproj ./src/
# Use the Secret Mount to safely handle the token
# The id 'app_token' must match the CLI --secret id
RUN --mount=type=secret,id=app_token \
TOKEN=$(cat /run/secrets/app_token) && \
dotnet nuget add source "$PACKAGE_SOURCE_URL" \
--name "$SOURCE_NAME" \
--username "$SOURCE_USER" \
--password "$TOKEN" \
--store-password-in-clear-text && \
dotnet restore
# Copy remaining source and publish
COPY . .
RUN dotnet publish -c Release -o /app/output --no-restore
# --- Stage 2: Runtime ---
FROM mcr.microsoft.com/dotnet/runtime:10.0-bookworm-slim
WORKDIR /app
# Security: Create a non-root user
# 'useradd' is used instead of 'adduser' for compatibility with 'slim' images
RUN useradd -m -d /app -s /bin/bash appuser
# Copy output and set ownership
COPY --from=build-env --chown=appuser:appuser /app/output .
# Optional: Install diagnostics tools
RUN apt-get update && apt-get install -y --no-install-recommends curl \
&& rm -rf /var/lib/apt/lists/*
USER appuser
ENTRYPOINT ["dotnet", "MyApplication.dll"]
3. CI/CD Integration
A. GitHub Actions Implementation
GitHub Actions requires you to explicitly map a Secret to an environment variable in the shell before Docker can consume it.
- name: Build Docker Image
env:
# Map the GitHub Secret to a shell environment variable
READONLY_TOKEN: ${{ secrets.ORG_READONLY_TOKEN }}
DOCKER_BUILDKIT: 1 # Ensure BuildKit is enabled
run: |
docker build \
--secret id=app_token,env=READONLY_TOKEN \
--build-arg PACKAGE_SOURCE_URL="${{ secrets.SOURCE_URL }}" \
--build-arg SOURCE_NAME="InternalFeed" \
--build-arg SOURCE_USER="${{ secrets.SOURCE_USER }}" \
-t my-app:latest .
B. AWS CodeBuild Implementation
CodeBuild retrieves secrets from AWS Secrets Manager and populates them as environment variables automatically.
version: 0.2
env:
# Pulls directly from AWS Secrets Manager
secrets-manager:
BUILD_TOKEN: "path/to/secret:key_name"
phases:
build:
commands:
- export DOCKER_BUILDKIT=1
- |
docker build \
--secret id=app_token,env=BUILD_TOKEN \
--build-arg PACKAGE_SOURCE_URL="${SOURCE_URL}" \
--build-arg SOURCE_NAME="InternalRepo" \
--build-arg SOURCE_USER="${SOURCE_USER}" \
-f Dockerfile \
-t $REPOSITORY_URI:latest .
4. Critical Troubleshooting Guide
| Issue | Root Cause | Solution |
|---|---|---|
adduser: not found |
Using a Debian "slim" image. | Use useradd (the low-level binary) instead of adduser. |
Both UserName and Password must be specified |
A variable passed to dotnet nuget is empty. |
1. Ensure ARG is declared after the FROM line. 2. Wrap all vars in double quotes "$VAR". |
Secret not found |
ID mismatch between CLI and Dockerfile. | Ensure --secret id=XYZ matches RUN --mount=type=secret,id=XYZ. |
Invalid Build Flag |
BuildKit is disabled. | Set DOCKER_BUILDKIT=1 or use docker buildx build. |
5. Best Practices Checklist
- [ ] Multi-stage builds: Always separate the SDK (Build) from the Runtime (Execution) to minimize attack surface.
- [ ] Non-root execution: Never run your application as
rootinside the container. - [ ] Layer Optimization: Copy
.csprojor.slnxfiles andrestorebefore copying the full source code to take advantage of layer caching. - [ ] Secret Hygiene: Never echo secrets to the console. To verify if a secret exists during debug, check its length:
echo ${#TOKEN}. - [ ] Quotes: Always wrap shell variables in double quotes to prevent word-splitting if a password or URL contains special characters.
Top comments (0)