Skip to content

Building a custom container

Write a Dockerfile on top of a Stack Harbor PaaS base template, build the image, push it to a registry, and deploy it onto the platform.

This page walks through the end-to-end flow of preparing a custom Docker image for the Stack Harbor PaaS: writing a Dockerfile on top of one of the platform’s base templates, building the image, pushing it to a registry, and pointing the platform at it. The worked example builds a WildFly application server, but the structure applies to any image — pick a base, layer your software, expose the right ports, declare an entry point.

There are three phases:

  • Compose the Dockerfile.
  • Build and push the image.
  • Deploy it onto an environment.

Compose the Dockerfile

A Dockerfile is a text manifest the Docker daemon executes to build an image. Each line is an instruction; the daemon runs them in order and snapshots the result.

1. Pick a base

Use the platform’s AlmaLinux 9 base — it carries the configuration the PaaS expects for monitoring, SSH, logging, and lifecycle.

FROM almalinux:9

For images that need a different distribution, see container image requirements for the list of supported bases.

2. Metadata and variables

LABEL carries metadata (maintainer, version, description). ENV declares environment variables that downstream RUN steps can interpolate and that the container will inherit at runtime.

LABEL maintainer="ops@example.com"
ENV WILDFLY_VERSION=36.0.0.Final
ENV ADMIN_USER=admin
ENV ADMIN_PASSWORD=admin

ADMIN_PASSWORD should be replaced after first deploy — bake a default that’s obviously a placeholder so it’s easy to find.

3. Install runtime dependencies

RUN executes shell commands during the build. For WildFly we need a JDK and tar to unpack the WildFly distribution.

RUN dnf -y install java-17-openjdk-devel tar && dnf -y update

4. Fetch and unpack the application

Download the WildFly distribution from the official release, unpack it under /opt, and clean up the archive.

RUN cd /opt \
    && curl -LO https://github.com/wildfly/wildfly/releases/download/$WILDFLY_VERSION/wildfly-$WILDFLY_VERSION.tar.gz \
    && tar xf wildfly-${WILDFLY_VERSION}.tar.gz \
    && rm wildfly-${WILDFLY_VERSION}.tar.gz

A stable path under /opt/wildfly keeps later configuration files version-agnostic.

RUN ln -s /opt/wildfly-$WILDFLY_VERSION /opt/wildfly

6. Server configuration file

WildFly reads its launch options from a sysconfig file. Bake the path, log location, and bind addresses into the image.

RUN echo -en "WILDFLY_SH=/opt/wildfly/bin/standalone.sh\n\
WILDFLY_SERVER_CONFIG=standalone.xml\n\
WILDFLY_CONSOLE_LOG=/var/log/wildfly/console.log\n\
WILDFLY_OPTS=\"-b 0.0.0.0 -bmanagement=0.0.0.0 -Djboss.management.http.port=4949 -Djboss.management.https.port=4848 -Djboss.server.log.dir=/var/log/wildfly/\"\n" \
    >> /etc/sysconfig/wildfly-standalone.conf

7. systemd unit

AlmaLinux 9 and WildFly 36 use systemd. Drop the WildFly-provided unit file into /etc/systemd/system.

RUN cp /opt/wildfly-$WILDFLY_VERSION/bin/systemd/wildfly-standalone.service /etc/systemd/system/wildfly.service

8. Service user, log directory, permissions

RUN systemctl enable wildfly \
    && mkdir -p /var/log/wildfly \
    && adduser wildfly \
    && chown -R wildfly:wildfly /opt/wildfly-$WILDFLY_VERSION /opt/wildfly /var/log/wildfly

9. Self-signed TLS for the admin panel

RUN cd /opt/wildfly/standalone/configuration \
    && keypass=password \
    && keytool -genkeypair -alias serverkey -keyalg RSA -keysize 4096 -validity 7360 \
        -keystore application.keystore -keypass $keypass -storepass $keypass \
        -dname "cn=Server Administrator,o=Acme,c=GB" \
    && chown wildfly:wildfly application.keystore

Production deployments should replace this with a real certificate — the self-signed key is only acceptable for the initial bring-up.

10. Admin user

RUN /opt/wildfly/bin/add-user.sh --user $ADMIN_USER --password $ADMIN_PASSWORD --silent --enable

11. Locale, port exposure, entry point

RUN localedef -i en_US -f UTF-8 en_US.UTF-8

EXPOSE 22 80 443 8080 8743 9990 9993 8009 4848 4949

ENTRYPOINT ["/bin/bash"]

EXPOSE declares the ports the image opens — the platform uses this list to drive its container DNS and load-balancer wiring. ENTRYPOINT defines the process that runs when the container starts; see container run configuration for how the dashboard maps to ENTRYPOINT and CMD.

Build and push the image

Once the Dockerfile is in place, build and push from a workstation that has Docker installed.

1. Build

sudo docker build -t {image_name} {dockerfile_location}
  • {image_name} — the repository name plus an optional tag (e.g. examplerepo/wildfly:latest). Without a tag, Docker defaults to latest.
  • {dockerfile_location} — a local path or a URL. . if the Dockerfile is in the current directory.

2. Verify

sudo docker images

The new image should be in the list.

3. Push

sudo docker push {image_name}

{image_name} is the same string used during the build. The first push asks for registry credentials; you can avoid the prompt by running docker login ahead of time — credentials are then stored at ~/.docker/config.json.

Deploy onto the platform

Once the image is in the registry, point a Stack Harbor PaaS environment at it through the topology wizard.

  1. Click New Environment in the dashboard. Switch to the Custom tab. Click Select Image.
  2. Switch to the Custom sub-tab (or Search if the image is on public Docker Hub). Click Add New Image.
  3. Fill Name with [{registry_hostname}/]{namespace}/{image_name} — the registry host is optional for Docker Hub, required everywhere else.
  4. For a private registry, fill Username and Password. Otherwise leave them empty.
  5. Click Add. The image appears in the Custom list.
  6. Select the image onto the desired layer (typical choice for an app server: the Application Servers layer; the platform’s shared load balancer fronts it automatically). Configure the rest of the layer — instance count, vertical resize, mount points, environment variables.
  7. Click Create.

The environment provisions in a few minutes. Open in Browser on the layer opens the application’s domain.

For WildFly specifically, the welcome page resolves at the default domain, and the admin console is reachable through the link on that page. Log in with the credentials baked into the Dockerfile (admin/admin in this example) — rotate them immediately.

See also