Version: 1.0.0
Last Updated: December 2024
Maintainer: ContainerCraft
- Header Documentation
- Base Image and Metadata
- Environment Variables
- Package Installation
- Tool Installation Pattern
- User and Permission Management
- Directory Structure
- Cleanup and Optimization
- Multi-Stage Builds
- Formatting Standards
- Testing and Validation
- Image Metadata
Every Dockerfile MUST begin with a standardized header comment block:
###############################################################################
# Use:
# - docker build --progress plain --tag ghcr.io/[org]/[image]:[tag] -f docker/[variant]/Dockerfile ./docker
# - docker run --rm -d --name [container] --hostname [hostname] ghcr.io/[org]/[image]:[tag]
###############################################################################
- Use 79
#
characters for separator lines - Include both
docker build
anddocker run
example commands - Use
--progress plain
for build visibility - Include the full registry path in tags
- Specify the Dockerfile path with
-f
flag - Include essential run flags (
--rm
,-d
,--name
,--hostname
)
Immediately after the header, define the base image and initial metadata:
FROM ghcr.io/containercraft/devcontainer:base
LABEL tag="variant-name"
ENV DEVCONTAINER="variant-name"
- Use specific, versioned base images when possible
- Set both
LABEL tag
andENV DEVCONTAINER
to the same variant name - Place these declarations immediately after the FROM statement
Define environment variables in logical groups with clear purposes:
# Disable LC_ALL for Nix compatibility
ENV LC_ALL=""
# Disable timezone prompts
ENV TZ=UTC
# Set TERM to linux to avoid prompts
ENV TERM=linux
# Disable package manager prompts
ENV DEBIAN_FRONTEND=noninteractive
# Add go and nix to path
ENV PATH="/home/ubuntu/.krew/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/go/bin:/nix/var/nix/profiles/default/bin"
Define reusable command patterns as environment variables:
# Common Dockerfile Container Build Functions
ENV apt_update="sudo apt-get update"
ENV apt_install="TERM=linux DEBIAN_FRONTEND=noninteractive sudo apt-get install -q --yes --purge --assume-yes --auto-remove --allow-downgrades -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold'"
ENV apt_clean="sudo apt-get clean && sudo apt-get autoremove -y && sudo apt-get purge -y --auto-remove"
ENV curl="/usr/bin/curl --silent --show-error --tlsv1.2 --location"
ENV curl_github="/usr/bin/curl --silent --show-error --tlsv1.2 --request GET --url"
ENV dir_clean="\
sudo rm -rf \
/var/lib/{apt,cache,log} \
/usr/share/{doc,man,locale} \
/var/cache/apt \
/home/*/.cache \
/root/.cache \
/var/tmp/* \
/tmp/* \
"
- Group related environment variables with descriptive comments
- Use full command paths where applicable
- Include all necessary flags for non-interactive operation
- Use line continuation (
\
) for multi-line values - Define cleanup commands as reusable variables
Use clear section headers with 80+ #
characters:
#################################################################################
# Base package and user configuration
#################################################################################
Define package lists using ARG for flexibility:
# Apt Packages
ARG APT_PKGS="\
gh \
git \
tar \
curl \
tmux \
"
RUN echo \
&& export TEST="[command] --version" \
&& ${apt_update} \
&& bash -c "${apt_install} --no-install-recommends -o Dpkg::Options::='--force-confold' ${APT_PKGS}" \
&& bash -c "${apt_clean}" \
&& ${dir_clean} \
&& ${TEST} \
&& echo
- Start and end with
echo
for clean output - Define a TEST command for validation
- Use environment variables for commands
- Include cleanup steps
- Execute the test command
- One package per line in ARG lists with trailing backslash
For external tools, use this standardized pattern:
RUN echo \
&& export NAME=toolname \
&& export TEST="${NAME} --version" \
&& export REPOSITORY="owner/repo" \
&& export VERSION="$(${curl} https://api.github.com/repos/${REPOSITORY}/releases/latest | jq --raw-output .tag_name)" \
&& export ARCH=$(uname -m | awk '{ if ($1 == "x86_64") print "amd64"; else if ($1 == "aarch64" || $1 == "arm64") print "arm64"; else print "unknown" }') \
&& export PKG="${NAME}-linux-${ARCH}.tar.gz" \
&& export URL="https://github.com/${REPOSITORY}/releases/download/${VERSION}/${PKG}" \
&& echo "---------------------------------------------------------"\
&& echo "INFO[${NAME}] Installed:" \
&& echo "INFO[${NAME}] Command: ${NAME}" \
&& echo "INFO[${NAME}] Package: ${PKG}" \
&& echo "INFO[${NAME}] Latest Release: ${VERSION}" \
&& echo "INFO[${NAME}] Architecture: ${ARCH}" \
&& echo "INFO[${NAME}] Source: ${URL}" \
&& echo "---------------------------------------------------------"\
&& ${curl} ${URL} --output /tmp/${NAME} \
&& sudo ${INSTALL} /tmp/${NAME} ${BIN}/${NAME} \
&& ${dir_clean} \
&& ${TEST} \
&& echo
- Use consistent variable names (NAME, TEST, REPOSITORY, VERSION, ARCH, PKG, URL)
- Include architecture detection
- Display installation information with formatted output
- Use 57
-
characters for separators - Download to
/tmp
and install to${BIN}
- Clean up after installation
- Test the installation
RUN echo \
&& mkdir -p /etc/sudoers.d || true \
&& groupadd --force --system sudo || true \
&& groupadd --force --gid 127 --system docker \
&& echo "%sudo ALL=(ALL:ALL) NOPASSWD: ALL" | tee /etc/sudoers.d/sudo \
&& echo
RUN echo \
&& export USER_ID="1000" \
&& export USER_NAME="ubuntu" \
&& export USER_SHELL="bash" \
&& export USER_GROUPS="${USER_NAME},sudo,docker,ubuntu" \
&& export USER_GROUP_NAME="${USER_NAME}" \
&& export USER_GROUP_ID="${USER_ID}" \
&& echo "INFO[${USER_NAME}] User:" \
&& echo "INFO[${USER_NAME}] User Name: ${USER_NAME}" \
&& echo "INFO[${USER_NAME}] User Group: ${USER_GROUP_NAME}" \
&& echo "INFO[${USER_NAME}] Aux Groups: ${USER_GROUPS}" \
&& echo "INFO[${USER_NAME}] Group ID: ${USER_GROUP_ID}" \
&& echo "INFO[${USER_NAME}] User ID: ${USER_ID}" \
&& echo "INFO[${USER_NAME}] SHELL: $(which ${USER_SHELL})" \
&& sudo groupadd --force --gid ${USER_ID} ${USER_NAME} \
&& sudo useradd --create-home --uid ${USER_ID} --gid ${USER_GROUP_ID} --shell $(which ${USER_SHELL}) --groups ${USER_GROUPS} ${USER_NAME} || true \
&& echo
- Always use
|| true
for operations that might fail safely - Display user configuration details
- Use consistent UID/GID (1000 for primary user, 1001+ for additional users)
- Set proper shell with
$(which ${USER_SHELL})
# For directory trees
ADD slim/rootfs /
ADD slim/rootfs/etc/skel/ /root/
ADD slim/rootfs/etc/skel/ /home/ubuntu/
# For individual files
COPY ./bin/entrypoint /bin/
- Use
ADD
for directory structures - Use
COPY
for individual files - Maintain consistent source paths
Always clean up after operations:
&& ${dir_clean} \
This should remove:
- APT caches:
/var/lib/{apt,cache,log}
- Documentation:
/usr/share/{doc,man,locale}
- User caches:
/home/*/.cache
,/root/.cache
- Temporary files:
/var/tmp/*
,/tmp/*
FROM docker.io/gitlab/glab:latest as glab
FROM ghcr.io/containercraft/devcontainer:dind
COPY --from=glab /usr/bin/glab /usr/local/bin/glab
- Use lowercase names for stages
- Copy specific files, not entire directories when possible
RUN echo \
&& command1 \
&& command2 \
&& echo
- Header/footer: 79
#
characters - Section headers: 80+
#
characters - Info output: 57
-
characters
- Single blank line between major sections
- No blank lines within RUN commands
- Align
\
characters when possible
# Single line comment for simple explanations
##################################################################################
# Major Section Header
# - Additional context line 1
# - Additional context line 2
Every installation should define and execute a test:
&& export TEST="${NAME} --version" \
... (installation steps) ...
&& ${TEST} \
--version
(preferred)version
(alternative)--help
(fallback)command -v [name]
(for commands without version flags)
#################################################################################
# Image Metadata
LABEL name="ContainerCraft Konductor Devcontainer"
LABEL io.k8s.display-name="ContainerCraft Konductor Devcontainer"
LABEL org.opencontainers.image.authors="github.com/ContainerCraft"
LABEL org.opencontainers.image.source="https://github.com/containercraft/devcontainer"
LABEL org.opencontainers.image.licenses="APACHE-2.0"
LABEL distribution-scope="public"
ENTRYPOINT ["/usr/bin/bash", "-c", "entrypoint"]
HEALTHCHECK --interval=120s --timeout=30s --start-period=5s --retries=3 CMD [ "true" ]
- Initial release
- Extracted patterns from ContainerCraft devcontainer Dockerfiles
- Established core formatting and structure requirements
- Defined standard installation patterns
- Created consistent variable naming conventions
- VSCode extension for linting/formatting
- Automated validation tools
- Additional patterns for specific use cases
- Performance optimization guidelines
- Security scanning integration patterns