A secure, ultra-minimal Alpine Linux base image with essential system utilities for building lightweight containerized applications.
Pull the latest version of this image from the Ghost registry. Pulling requires authentication — generate a token and run docker login first (see Authentication below).
The Ghost catalog is public to browse, but pulling images requires an account. Generate a pull token below (or from your Account → Tokens page) — you'll get a ready-to-paste docker login command, then docker pull works.
The username is generated automatically (it looks like robot$<project>+<auto-id>, not the name you typed) and is included in the docker login command above. The secret is shown only once when you create the token.
All Ghost images are signed with cosign. Verifying the signature before deployment ensures the image has not been tampered with.
Install cosign via brew install cosign or download from the Sigstore releases page.
Reference this image in your Dockerfile as a base layer:
All examples in this guide use the public image. If you've mirrored the repository for your own use (for example, to your Docker Hub namespace), update your commands to reference the mirrored image instead of the public one.
For example:
registry.ghost-prod.alphabravo.io/ghost-base/alpine-base:<tag><your-namespace>/dhi-alpine-base:<tag>For the examples, you must first use docker login registry.ghost-prod.alphabravo.io to authenticate to the registry to pull the images.
This Docker Hardened alpine-base image provides a minimal, security-hardened Alpine Linux foundation built on Docker Hardened Images/Alpine Linux v3.23:
busybox: A single binary that provides lightweight implementations of common Unix utilities including sh, ls,
cp, mv, wget, and more - all essential for shell scripting and container toolingmusl libc: A lightweight, standards-compliant C standard library used as the foundation for all Alpine-based
binaries/bin/ash and /bin/sh: POSIX-compatible shells provided by BusyBox, available in both the runtime and dev variantsapk package manager: Available in the dev variant only (-dev tag) for installing additional packages during build
stages - the runtime variant does not include apk to minimise attack surface/etc/ssl/certs/ca-certificates.crt for secure
outbound TLS connections without additional setupnonroot user (UID 65532) for running workloads without root privileges in the runtime variantOn startup, the image initializes BusyBox and Alpine utilities.
Run the following command and replace <tag> with the image variant you want to run:
$ docker run -it --rm registry.ghost-prod.alphabravo.io/ghost-base/alpine-base:<tag> sh
Note: The runtime variant includes
/bin/ashand a shell. To use the dev variant for build-time operations, use a tag that includes-dev(for example,3.23-alpine3.23-dev).
Use alpine-base as the foundation for lightweight, minimal containers.
Create a sample application and Dockerfile:
cat > myapp << 'EOF'
#!/bin/sh
echo "hello from alpine-base DHI"
EOF
chmod +x myapp
cat > Dockerfile << 'EOF'
FROM registry.ghost-prod.alphabravo.io/ghost-base/alpine-base:<tag>
COPY myapp /usr/local/bin/myapp
USER nonroot
CMD ["/usr/local/bin/myapp"]
EOF
Build and run:
$ docker build -t alpine-base-minimal-test .
$ docker run --rm alpine-base-minimal-test
hello from alpine-base DHI
Verify the container runs as nonroot:
$ docker inspect alpine-base-minimal-test --format='User: {{.Config.User}} | Cmd: {{.Config.Cmd}}'
User: nonroot | Cmd: [/usr/local/bin/myapp]
$ docker run --rm --entrypoint /bin/ash alpine-base-minimal-test -c 'id'
uid=65532(nonroot) gid=65532(nonroot) groups=65532(nonroot),65532(nonroot)
Use the dev variant with apk to install testing tools in a build stage. Because curl is dynamically linked on Alpine
with a deep transitive library chain, copy the entire /usr/lib from the build stage rather than individual libraries —
this is safe because DHI's /usr/lib is already stripped to near-minimum.
Create the Dockerfile:
cat > Dockerfile.security-test << 'EOF'
FROM registry.ghost-prod.alphabravo.io/ghost-base/alpine-base:<tag>-dev AS build
RUN apk add --no-cache curl ca-certificates
FROM registry.ghost-prod.alphabravo.io/ghost-base/alpine-base:<tag>
COPY --from=build /usr/lib/ /usr/lib/
COPY --from=build /usr/bin/curl /usr/bin/curl
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
USER nonroot
EOF
Build and verify:
$ docker build -t alpine-base-security-test -f Dockerfile.security-test .
$ docker run --rm --entrypoint /usr/bin/curl alpine-base-security-test --version
curl 8.19.0 (aarch64-alpine-linux-musl) libcurl/8.19.0 OpenSSL/3.5.5 zlib/1.3.2 ...
$ docker run --rm --entrypoint /bin/ash alpine-base-security-test -c 'id'
uid=65532(nonroot) gid=65532(nonroot) groups=65532(nonroot),65532(nonroot)
Note: Copying individual libraries is fragile on Alpine —
curlhas a deep transitive dependency chain (libcurl→libssl,libcrypto,libnghttp2,libbrotli*,libzstd,libcares,libidn2,libpsl,libunistring) that changes between versions. Copying/usr/lib/as a whole is the reliable approach. Verified againstcurl 8.19.0-r0on Alpine 3.23 (aarch64).
Use the dev variant to build your application, then copy the statically compiled binary to the runtime image. This
example uses Go with CGO_ENABLED=0 to produce a static binary that requires no shared libraries.
Create the application and Dockerfile:
mkdir -p goapp
cat > goapp/main.go << 'EOF'
package main
import "fmt"
func main() {
fmt.Println("hello from alpine-base DHI")
}
EOF
cat > goapp/go.mod << 'EOF'
module server
go 1.21
EOF
cat > goapp/Dockerfile << 'EOF'
FROM registry.ghost-prod.alphabravo.io/ghost-base/alpine-base:<tag>-dev AS build
RUN apk add --no-cache go
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o server .
FROM registry.ghost-prod.alphabravo.io/ghost-base/alpine-base:<tag>
COPY --from=build /app/server /usr/local/bin/server
USER nonroot
CMD ["/usr/local/bin/server"]
EOF
Build and run:
$ docker build -t alpine-base-go-test goapp/
$ docker run --rm alpine-base-go-test
hello from alpine-base DHI
Verify the binary runs as nonroot:
$ docker run --rm --entrypoint /bin/ash alpine-base-go-test -c 'id'
uid=65532(nonroot) gid=65532(nonroot) groups=65532(nonroot),65532(nonroot)
For authentication setup, see Use a Ghost hardened image in Kubernetes.
Create the namespace and imagePullSecret:
$ kubectl create namespace alpine-base-test
$ kubectl create secret docker-registry dhi-registry-secret \
--docker-server=registry.ghost-prod.alphabravo.io \
--docker-username=<your-docker-username> \
--docker-password=<your-access-token> \
--namespace=alpine-base-test
Write the Deployment manifest:
cat > alpine-base-deployment.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: alpine-base-deployment
namespace: alpine-base-test
spec:
replicas: 1
selector:
matchLabels:
app: alpine-base
template:
metadata:
labels:
app: alpine-base
spec:
imagePullSecrets:
- name: dhi-registry-secret
containers:
- name: alpine-base
image: registry.ghost-prod.alphabravo.io/ghost-base/alpine-base:<tag>
securityContext:
runAsNonRoot: true
nodeSelector:
kubernetes.io/os: linux
EOF
Deploy and verify:
$ kubectl apply -f alpine-base-deployment.yaml
$ kubectl get pods -n alpine-base-test
Note: The runtime image runs as the
nonrootuser (UID 65532) by default. The deployment usesrunAsNonRoot: trueto enforce this. If your workload requires root, setrunAsUser: 0explicitly, but this is not recommended for production.
| Feature | DOI (library/alpine) | DHI (registry.ghost-prod.alphabravo.io/ghost-base/alpine-base) |
|---|---|---|
| User | root | nonroot / UID 65532 (runtime) / root (dev) |
| Shell | /bin/ash | /bin/ash (runtime) / /bin/ash (dev) |
| Package manager | apk | No (runtime) / apk (dev) |
| Entrypoint | /bin/sh | None (CMD-only) |
| Uncompressed size | ~5MB | ~4MB |
| Zero CVE commitment | No | Yes |
| FIPS variant | No | Yes (FIPS + STIG + CIS) |
| Base OS | Alpine Linux | Ghost hardened images/Alpine Linux v3.23 |
| Compliance labels | None | CIS (runtime), FIPS+STIG+CIS (fips) |
| ENV: PATH | /usr/sbin:/sbin:/bin | /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin |
Ghost hardened images come in different variants depending on their intended use. Image variants are identified by their tag.
Runtime variants — designed for production use. These images:
nonroot user (UID 65532) by default/bin/ash and /bin/shapk)Dev variants (tag includes -dev) — intended for use in build stages of a multi-stage Dockerfile. These images:
root user/bin/ash, /bin/sh, and the apk package manager (apk-tools 3.0.5-r0)FIPS variants (tag includes -fips) — for environments requiring FIPS 140 compliance. Available in both runtime
and dev flavours. These images carry CIS, FIPS, and STIG (100%) compliance badges.
The following tags are available for Alpine 3.23:
| Variant | Tags | User | Compliance | Support until |
|---|---|---|---|---|
| Runtime | 3.23-alpine3.23, 3.23 | nonroot | CIS | Nov 2027 |
| Dev | 3.23-alpine3.23-dev, 3.23-dev | root | CIS | Nov 2027 |
| FIPS runtime | 3.23-alpine3.23-fips, 3.23-fips | nonroot | CIS, FIPS, STIG | Nov 2027 |
| FIPS dev | 3.23-alpine3.23-fips-dev, 3.23-fips-dev | root | CIS, FIPS, STIG | Nov 2027 |
Note: Pulling FIPS variants requires a Docker subscription.
To view all available tags, select the Tags tab for this repository.
To migrate your application to a Ghost hardened image, you must update your Dockerfile. At minimum, you must update the base image in your existing Dockerfile to a Ghost hardened image. This and a few other common changes are listed in the following table of migration notes:
| Item | Migration note |
|---|---|
| Base image | Replace your base images in your Dockerfile with a Ghost hardened image. |
| Package management | Non-dev images, intended for runtime, don't contain package managers. Use package managers only in images with a dev tag. |
| Non-root user | By default, runtime images run as the nonroot user (UID 65532). Ensure that necessary files and directories are accessible to the nonroot user. |
| Multi-stage build | Use images with a dev tag for build stages and non-dev images for runtime. For binary executables, use a static image for runtime. |
| TLS certificates | Ghost hardened images contain standard TLS certificates by default. There is no need to install TLS certificates. |
| Ports | Runtime images run as the nonroot user by default. As a result, applications in these images can't bind to privileged ports (below 1024) when running in Kubernetes or in Docker Engine versions older than 20.10. To avoid issues, configure your application to listen on port 1025 or higher inside the container. |
| Entry point | The DHI alpine-base image has no configured entrypoint (unlike some Docker Official Images that default to /bin/sh). Use CMD to specify your application command, or inspect the entry point with docker inspect and update your Dockerfile if necessary. |
| Shell | The runtime image includes /bin/ash. However, the runtime image does not include a package manager. Use dev images in build stages to install packages and copy artifacts to the runtime stage. |
The following steps outline the general migration process.
Find hardened images for your app.
A hardened image may have several variants. Inspect the image tags and find the image variant that meets your needs.
Update the base image in your Dockerfile.
Update the base image in your application's Dockerfile to the hardened image you found in the previous step. For
framework images, this is typically going to be an image tagged as dev because it has the tools needed to install
packages and dependencies.
For multi-stage Dockerfiles, update the runtime image in your Dockerfile.
To ensure that your final image is as minimal as possible, use a multi-stage build. All stages in your Dockerfile
should use a hardened image. While intermediary stages will typically use images tagged as dev, your final runtime
stage should use a non-dev image variant.
Install additional packages.
Ghost hardened images contain minimal packages in order to reduce the potential attack surface. You may need to install additional packages in your Dockerfile. Inspect the image variants to identify which packages are already installed.
Only images tagged as dev have package managers. Use a multi-stage Dockerfile to install packages in the build
stage using a dev image, then copy only the necessary artifacts to the runtime stage.
For Alpine-based images, use apk to install packages in the dev stage. For Debian-based images, use apt-get.
The recommended method for debugging applications built with Ghost hardened images is to use Docker Debug to attach to these containers. Docker Debug provides a shell, common debugging tools, and lets you install other tools in an ephemeral, writable layer that only exists during the debugging session.
By default, runtime image variants run as the nonroot user (UID 65532). Ensure that necessary files and directories
are accessible to the nonroot user. You may need to copy files to different directories or change permissions so your
application can access them.
Runtime images run as the nonroot user by default. As a result, applications in these images can't bind to privileged
ports (below 1024) when running in Kubernetes or in Docker Engine versions older than 20.10.
To avoid issues, configure your application to listen on port 1025 or higher inside the container, even if you map it to
a lower port on the host. For example, docker run -p 80:8080 my-image works because the port inside the container is
8080, but docker run -p 80:81 my-image won't because the port inside the container is 81.
The runtime image includes /bin/ash. However, the runtime image does not include a package manager. Use dev images
in build stages to install packages with apk, then copy any necessary artifacts into the runtime stage. To debug a
running container interactively, use Docker Debug.
The DHI alpine-base image has no configured entrypoint. Use docker inspect to confirm:
$ docker inspect --format='{{json .Config.Entrypoint}}' registry.ghost-prod.alphabravo.io/ghost-base/alpine-base:<tag>
null
If your application previously relied on an entrypoint being set by the base image, add an explicit ENTRYPOINT
instruction to your Dockerfile.