Extending Coolify Templates with dockerfile_inline: A Keycloak Theme Example
When running self-hosted applications through Coolify, you'll often encounter situations where the default templates need customization. Whether it's adding a custom theme, installing plugins, or bundling additional dependencies, the dockerfile_inline feature in Docker Compose provides an elegant solution without maintaining separate Dockerfile repositories.
In this guide, we'll walk through a practical example: packaging the beautiful Keywind theme for Keycloak as a JAR file and integrating it into a Coolify deployment.
Chapter 1: Building the Keywind Theme JAR
The Keywind theme is a modern, Tailwind CSS-based theme for Keycloak. To use it as a provider JAR (the recommended approach for Keycloak 17+), we need to package it correctly.
Prerequisites
- Node.js (v18 or later)
- pnpm (or npm/yarn)
- Basic command line knowledge
Step 1: Clone and Build the Theme
First, clone the original Keywind repository and install dependencies:
git clone https://github.com/lukin/keywind.git
cd keywind
pnpm install
Build the theme files:
pnpm build
This generates the compiled theme files in the theme/keywind directory.
Step 2: Create the JAR Structure
Keycloak expects theme JARs to follow a specific structure. Create a new directory for packaging:
mkdir -p keycloak-theme-jar/META-INF/keycloak-themes
mkdir -p keycloak-theme-jar/theme/keywind
Step 3: Copy Theme Files
Copy the built theme into the JAR structure:
cp -r theme/keywind/* keycloak-theme-jar/theme/keywind/
Step 4: Create the Theme Descriptor
Keycloak needs a keycloak-themes.json file to recognize the theme. Create it at keycloak-theme-jar/META-INF/keycloak-themes/keycloak-themes.json:
{
"themes": [
{
"name": "keywind",
"types": ["login"]
}
]
}
This tells Keycloak that our JAR contains a theme named "keywind" that provides a "login" theme type.
Step 5: Package as JAR
Navigate to the packaging directory and create the JAR:
cd keycloak-theme-jar
jar cvf keywind-theme.jar .
Your final directory structure should look like this:
keycloak-theme-jar/
├── META-INF/
│ └── keycloak-themes/
│ └── keycloak-themes.json
├── theme/
│ └── keywind/
│ └── login/
│ ├── resources/
│ │ ├── css/
│ │ └── ...
│ ├── theme.properties
│ └── ...
└── keywind-theme.jar
Step 6: Host the JAR
Upload your keywind-theme.jar to a publicly accessible location. In this example, we're using GitHub:
- Create a new repository (e.g.,
Keycloak-theme) - Upload the JAR file to the repository
- Note the raw file URL:
https://github.com/YOUR_USERNAME/Keycloak-theme/raw/main/keywind-theme.jar
The complete example is available at github.com/mik9016/Keycloak-theme.
Chapter 2: The Default Coolify Keycloak Template
Coolify provides a ready-to-use Keycloak template. Here's what the default Docker Compose looks like:
services:
keycloak:
image: 'quay.io/keycloak/keycloak:26.1'
command:
- start
environment:
- SERVICE_URL_KEYCLOAK_8080
- 'TZ=${TIMEZONE:-UTC}'
- 'KC_BOOTSTRAP_ADMIN_USERNAME=${SERVICE_USER_ADMIN}'
- 'KC_BOOTSTRAP_ADMIN_PASSWORD=${SERVICE_PASSWORD_ADMIN}'
- 'KC_HOSTNAME=${SERVICE_URL_KEYCLOAK}'
- 'KC_HTTP_ENABLED=${KC_HTTP_ENABLED:-true}'
- 'KC_HEALTH_ENABLED=${KC_HEALTH_ENABLED:-true}'
- 'KC_PROXY_HEADERS=${KC_PROXY_HEADERS:-xforwarded}'
volumes:
- 'keycloak-data:/opt/keycloak/data'
- './themes/keywind:/opt/keycloak/themes/keywind'
healthcheck:
test:
- CMD-SHELL
- "exec 3<>/dev/tcp/127.0.0.1/9000; echo -e 'GET /health/ready HTTP/1.1\r\nHost: localhost:9000\r\nConnection: close\r\n\r\n' >&3;cat <&3 | grep -q '\"status\": \"UP\"' && exit 0 || exit 1"
interval: 5s
timeout: 20s
retries: 10
volumes:
keycloak-data:
This template works great for basic deployments, but has a limitation: the theme volume mount (./themes/keywind) requires the theme files to exist on the host filesystem. In Coolify's managed environment, this isn't practical.
Chapter 3: The Enhanced Template with dockerfile_inline
Here's the modified Docker Compose that automatically downloads and installs our theme JAR:
services:
keycloak:
build:
context: .
dockerfile_inline: |
FROM quay.io/keycloak/keycloak:26.1
ADD --chmod=644 https://github.com/mik9016/Keycloak-theme/raw/main/keywind-theme.jar /opt/keycloak/providers/keywind-theme.jar
RUN /opt/keycloak/bin/kc.sh build
command:
- start
environment:
- SERVICE_URL_KEYCLOAK_8080
- 'TZ=${TIMEZONE:-UTC}'
- 'KC_BOOTSTRAP_ADMIN_USERNAME=${SERVICE_USER_ADMIN}'
- 'KC_BOOTSTRAP_ADMIN_PASSWORD=${SERVICE_PASSWORD_ADMIN}'
- 'KC_HOSTNAME=${SERVICE_URL_KEYCLOAK}'
- 'KC_HTTP_ENABLED=${KC_HTTP_ENABLED:-true}'
- 'KC_HEALTH_ENABLED=${KC_HEALTH_ENABLED:-true}'
- 'KC_PROXY_HEADERS=${KC_PROXY_HEADERS:-xforwarded}'
volumes:
- 'keycloak-data:/opt/keycloak/data'
healthcheck:
test:
- CMD-SHELL
- "exec 3<>/dev/tcp/127.0.0.1/9000; echo -e 'GET /health/ready HTTP/1.1\r\nHost: localhost:9000\r\nConnection: close\r\n\r\n' >&3;cat <&3 | grep -q '\"status\": \"UP\"' && exit 0 || exit 1"
interval: 5s
timeout: 20s
retries: 10
volumes:
keycloak-data:
Chapter 4: Understanding dockerfile_inline
The dockerfile_inline feature allows you to embed Dockerfile instructions directly within your Docker Compose file. This eliminates the need for a separate Dockerfile while still enabling image customization.
The Syntax
build:
context: .
dockerfile_inline: |
FROM base-image:tag
# Your Dockerfile instructions here
The | character in YAML indicates a multi-line string, preserving line breaks exactly as written.
Breaking Down Our Dockerfile Instructions
Let's examine each line:
Line 1: FROM quay.io/keycloak/keycloak:26.1
This sets our base image to the official Keycloak 26.1 image. We're extending it rather than replacing it.
Line 2: ADD --chmod=644 https://github.com/.../keywind-theme.jar /opt/keycloak/providers/keywind-theme.jar
The ADD instruction downloads a file from a URL directly into the container. Key points:
--chmod=644sets read permissions for all users (required for Keycloak to load the provider)- The URL points to the raw JAR file on GitHub
/opt/keycloak/providers/is Keycloak's provider directory where it automatically discovers JAR extensions
Line 3: RUN /opt/keycloak/bin/kc.sh build
This crucial step runs Keycloak's build command, which:
- Discovers and registers all providers in the
/opt/keycloak/providers/directory - Optimizes the Keycloak installation for production
- Pre-compiles theme resources for better performance
Key Structural Changes
| Original Template | Enhanced Template |
|---|---|
image: 'quay.io/keycloak/keycloak:26.1' |
build: with dockerfile_inline |
| Theme mounted via volume | Theme embedded in image |
| Theme files required on host | Self-contained, portable |
Why This Approach?
- Portability: The entire configuration is self-contained in one file
- No External Dependencies: No need to manage theme files on the host
- Version Control: Your customizations are documented in the compose file
- Coolify Compatibility: Works seamlessly with Coolify's deployment model
Chapter 5: Activating the Theme in Keycloak
After deployment, activate the Keywind theme:
- Log into the Keycloak Admin Console
- Select your realm (or create a new one)
- Navigate to Realm Settings → Themes
- Under Login theme, select keywind
- Click Save
Your login pages will now use the modern Keywind design.
And here we go we can use our theme for login:

Conclusion
The dockerfile_inline feature bridges the gap between using pre-built templates and needing custom modifications. It's particularly powerful for self-hosted platforms like Coolify where you want the simplicity of templates with the flexibility of custom builds.
This pattern extends beyond themes. You can use the same approach to:
- Add custom Keycloak providers (authentication, federation, etc.)
- Install additional plugins for other applications
- Bundle configuration files or certificates
- Apply security patches to base images
The key insight is that you don't always need a separate repository or complex CI/CD pipeline. Sometimes, a few inline Dockerfile instructions are all you need.
Have questions or want to share your own dockerfile_inline use cases? Drop a comment below!