🏗️ Production and Integration
CI/CD Pipeline Optimization
The project is designed to keep Docker builds cache-friendly.
Recommended pattern:
- Download packages and large external resources in Dockerfile
RUNlayers. - Copy services into
/container/services. - Run
container services install && container services link. - Copy runtime environment files last.
That is why generated service documentation recommends:
- keep
install.shfor service installation logic only - keep heavy downloads outside
install.shwhen possible - use
startup.shfor runtime customization based on environment variables
Why this helps:
- Docker can reuse download layers even if a service script changes.
- Changing
install.shdoes not force external dependencies to be downloaded again if those downloads live in earlier Dockerfile layers. - Environment files are copied after service installation, so runtime config changes do not invalidate installation layers.
For CI jobs where write durability is not important, --fast-write can also help reduce package install and file operation overhead by disabling fsync and related sync operations through eatmydata.
Use it for short-lived build or test containers, not for cases where you need conservative disk-write guarantees.
Adding container to an Existing Image
You can also add the container binary to an existing image instead of starting directly from osixia/baseimage.
This is useful when:
- you already have a working image and want to adopt the lifecycle tooling incrementally
- you want to keep a custom base image but still use
containeras entrypoint - you want access to commands such as
container install,container services,container log, and the managed runtime lifecycle
Example:
FROM debian:trixie-slim
ARG OSIXIA_BASEIMAGE_VERSION="2.0.0-beta"
ARG OSIXIA_BASEIMAGE_ARCH="amd64"
RUN apt-get update && apt-get install -y nginx xz-utils
RUN echo "daemon off;" >> /etc/nginx/nginx.conf
CMD ["/usr/sbin/nginx"]
ADD https://github.com/osixia/container-baseimage/releases/download/${OSIXIA_BASEIMAGE_VERSION}/container_linux_${OSIXIA_BASEIMAGE_ARCH}.tar.gz /tmp
RUN tar -xf /tmp/container_linux_${OSIXIA_BASEIMAGE_ARCH}.tar.gz \
&& rm -rf /tmp/* \
&& mv container_linux_${OSIXIA_BASEIMAGE_ARCH} /usr/sbin/container \
&& /usr/sbin/container install
ENTRYPOINT ["/usr/sbin/container", "--"]
What this does:
- installs your application packages as usual
- downloads a released
containerbinary matching the target architecture - installs the baseimage helper files with
container install - switches the image entrypoint to the osixia runtime wrapper
In practice, once the binary is installed, the image can use the same runtime patterns as an image built directly from osixia/baseimage.
For full entrypoint support, it is better to define a real service with a process.sh script instead of relying only on a plain CMD.
That way the image can benefit from the full lifecycle and runtime controls, including:
--execand--skip--restart--step startup|process|finish--pre-startup-cmd,--pre-process-cmd,--pre-finish-cmd,--pre-exit-cmd--bash
Recommended pattern:
FROM debian:trixie-slim
ARG OSIXIA_BASEIMAGE_VERSION="2.0.0-beta"
ARG OSIXIA_BASEIMAGE_ARCH="amd64"
RUN apt-get update && apt-get install -y nginx xz-utils
ADD https://github.com/osixia/container-baseimage/releases/download/${OSIXIA_BASEIMAGE_VERSION}/container_linux_${OSIXIA_BASEIMAGE_ARCH}.tar.gz /tmp
RUN tar -xf /tmp/container_linux_${OSIXIA_BASEIMAGE_ARCH}.tar.gz \
&& rm -rf /tmp/* \
&& mv container_linux_${OSIXIA_BASEIMAGE_ARCH} /usr/sbin/container \
&& /usr/sbin/container install
RUN mkdir -p /container/services/nginx
COPY process.sh /container/services/nginx/process.sh
RUN chmod +x /container/services/nginx/process.sh \
&& container services link nginx
ENTRYPOINT ["/usr/sbin/container"]
Example process.sh:
Keep in mind:
- you are responsible for downloading the right binary for your target architecture
container installprepares the runtime environment, but your image still needs service files or commands that make sense for your application- if you keep a
CMD, it becomes the default command executed bycontainerentrypoint - if you want the richest integration with the baseimage runtime, prefer service scripts over a bare
CMD
Container Debugging
Useful runtime options:
--bash: starts Bash alongside services or a standalone command--log-level debug|trace--log-format console|json--fast-write: disablesfsyncviaeatmydatafor faster ephemeral writes
Useful package-management command:
container packages install --debug-tools --update: installs common debugging tools for the current distribution
Useful commands inside a running container:
container services statuscontainer processes statuscontainer processes stop <name>container processes start <name>container environment print
How debugging works internally:
- process PID files are stored under
/run/container/processes - a
.downfile is used to request a managed process stop - stale PID files are cleaned up on startup
- when the entrypoint is PID 1, it reaps zombie processes and can terminate remaining child processes on exit
Practical debugging examples:
Running Read-Only & Rootless Containers
The image supports non-root execution and can work with read-only container filesystems. If you want baseimage runtime process management to work normally, /run/container must be writable.
Why it is needed for process management:
- runtime state is stored in
/run/container - process PID files and stop markers are created there, including the state used by
container processes - the generator also writes there when used inside a container
- the entrypoint ensures
/run/container,/run/container/processes, and/run/container/generatorexist and tries to set them to0777
If your image or runtime flow does not rely on those runtime management features, you may not need /run/container to be writable. But process management and many baseimage features assume it is available.
Typical read-only runtime pattern:
Typical rootless runtime pattern with the provided non-root image:
Notes:
- the repository publishes a dedicated
nonrootimage target that switches to usernonrootwith uid/gid65532 - for most non-root use cases, prefer using the provided
nonrootimage variant directly - you can also create your own application user in your Dockerfile if you want explicit uid/gid values or service-specific ownership
- example:
# Add application group and user
ARG APP_GROUP_GID=10001
ARG APP_USER_UID=10001
RUN container groups add "${APP_GROUP_GID}" app \
&& container users add "${APP_USER_UID}" app --group-id "${APP_GROUP_GID}" --group-name app
- runtime package installation commands such as
container packages install --debug-tools --updategenerally require enough privileges to use the distribution package manager - if the container filesystem is read-only, any service that writes outside writable mounts will still fail
Container Logging
Use container log in service scripts when you want messages that follow the container log level and format.
Common commands:
container log info "Starting application"
container log warning "Configuration file is missing"
container log error "Startup failed"
container log debug "Resolved config: ${CONFIG_PATH}"
container log trace "Shell tracing enabled"
container log level can also be used in scripts to inspect the current log level or compare against it.
Show current level:
Comparison commands:
container log level eq trace
container log level ne info
container log level gt debug
container log level ge debug
container log level lt warning
container log level le info
Those commands exit with status 0 when the comparison is true and 1 otherwise, which makes them convenient in shell scripts.
Typical example:
This enables shell tracing only when the container log level is exactly trace.
Limitations
Current behavior has a few important constraints:
process.sharguments are shared across all running services; there is no per-service argument mapping- multi-process restart is coarse-grained: when restart is enabled, any running process is restarted after it exits, not only on specific exit codes
- runtime process management expects
/run/containerto be writable, includingcontainer processesstate tracking - pre-step commands are shell-split, not shell-evaluated; shell features require
sh -corbash -c startup.shandfinish.share sequential; onlyprocess.shruns concurrently- runtime package installation and debug tooling depend on the underlying distribution package manager and privileges
- environment files are loaded from
/container/environment, but existing process environment variables are applied again afterward, so runtime-provided environment variables win over.envvalues