Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
120c663
#2319: Initial podman setup, more iterations follow
jh-RLI May 19, 2026
99c2f74
docs(podman): add first-time setup and release deployment sections to…
jh-RLI May 19, 2026
0eb9af4
feat(podman): add quadlets for systemd-based deployment
jh-RLI May 20, 2026
03ab8bc
feat(podman): add production ontop service config and fix bind mount …
jh-RLI May 20, 2026
daac738
feat(podman): add production image build workflow and use registry im…
jh-RLI May 20, 2026
38925fa
fix(podman): resolve inter-container DNS and network configuration #2319
jh-RLI May 20, 2026
8395f31
docs(podman): document Ubuntu 22.04 CNI fixes and Quadlets comparison…
jh-RLI May 20, 2026
c5a90e7
fix(podman): align quadlet lookup service with compose setup #2319
jh-RLI May 22, 2026
04289a6
fix(podman): track .env.example files and document env variables #2319
jh-RLI May 22, 2026
08e812f
feat(podman): add nginx reverse proxy for HTTPS on port 443 #2319
jh-RLI Jun 11, 2026
d1a8e81
Merge branch 'develop' into feature-2319-add-podman-setup
jh-RLI Jun 11, 2026
a5c8375
refactor(podman): adapt nginx proxy for upstream TLS termination #2319
jh-RLI Jun 11, 2026
1cfa2bf
reorder docker commands
jh-RLI Jul 1, 2026
45f3071
add latest oeo to podman image
jh-RLI Jul 1, 2026
9eb9542
fix(podman): bake OEO artifacts before collectstatic in production im…
jh-RLI Jul 1, 2026
bb7a49a
docs(podman): clarify ontop JDBC driver location, keep it out of git …
jh-RLI Jul 1, 2026
6f4ca15
build(podman): exclude ontologies/ and media/ from Docker build conte…
jh-RLI Jul 1, 2026
852cfb2
fix(podman): nginx listens on 443 (plain HTTP) for upstream TLS #2319
jh-RLI Jul 1, 2026
0587497
feat(ontop): auto-download JDBC driver and bake config into the image…
jh-RLI Jul 1, 2026
37998db
refactor(ontop): configure connection via env vars, drop ontop.proper…
jh-RLI Jul 1, 2026
b194f1b
docs(ontop): document self-provisioning and env-based config #2319
jh-RLI Jul 1, 2026
63faaa2
fix(podman): resolve lookup config path automatically, drop symlink #…
jh-RLI Jul 1, 2026
8aed71a
feat(podman): add idempotent install-nginx.sh for the host reverse pr…
jh-RLI Jul 1, 2026
dfaf712
fix(podman): make install-nginx.sh work under a separate sudo admin #…
jh-RLI Jul 1, 2026
6139d92
fix(settings): normalise scheme-less CSRF trusted origins #2319
jh-RLI Jul 1, 2026
92740c1
fix(podman): terminate TLS at nginx for the re-encrypting upstream pr…
jh-RLI Jul 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,9 @@ env*
oeplatform/securitysettings.py

node_modules/

# Runtime artifacts — must NOT be copied into the build context. The image bakes
# the OEO release + oeo_ext.owl itself (see podman/Dockerfile); a local copy here
# would bloat the context and collide with the baked version.
ontologies/
media/
106 changes: 106 additions & 0 deletions .github/workflows/build-production-image.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# SPDX-FileCopyrightText: 2025 Jonas Huber <https://github.com/jh-RLI> © Reiner Lemoine Institut
#
# SPDX-License-Identifier: AGPL-3.0-or-later
#
# Builds and pushes production container images to ghcr.io on every v* tag.
#
# Images produced:
# ghcr.io/openenergyplatform/oeplatform-production:<version> (app + Vite build)
# ghcr.io/openenergyplatform/oeplatform-ontop:<version> (Ontop + JDBC driver)
#
# The existing image-build.yaml continues to build the CI/testing image
# (ghcr.io/openenergyplatform/oeplatform) from docker/Dockerfile unchanged.

name: Build and publish production images

on:
push:
tags:
- "v*"
workflow_dispatch:

env:
REGISTRY: ghcr.io
ORG: openenergyplatform
# PostgreSQL JDBC driver version baked into the ontop image
JDBC_VERSION: "42.7.3"

jobs:
build-app:
name: OEPlatform app image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Log in to container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.ORG }}/oeplatform-production
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest

- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
file: ./podman/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

build-ontop:
name: Ontop image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Log in to container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.ORG }}/oeplatform-ontop
tags: |
type=semver,pattern={{version}}
type=raw,value=latest

- name: Build and push
uses: docker/build-push-action@v6
with:
context: ./docker
file: ./docker/Dockerfile.ontop
push: true
build-args: |
JDBC_VERSION=${{ env.JDBC_VERSION }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ venv*/
/envs
/node_env
.env*
!.env.example
!**/oep.env.example
/fuseki
apache*
/oep-django-5
Expand All @@ -93,7 +95,9 @@ apache*
# Docker
.node_modules_stamp
.vite
docker/serviceConfigs/ontop/postgresql.jar
# PostgreSQL JDBC driver for Ontop — provided manually, never committed
# (matches docker/ and podman/ service config dirs)
**/serviceConfigs/ontop/postgresql.jar

# Deployment files
docker/oeplatform_data
Expand Down
42 changes: 36 additions & 6 deletions docker/Dockerfile.ontop
Original file line number Diff line number Diff line change
@@ -1,8 +1,38 @@
# Use the official Ontop image as the base
FROM ontop/ontop:latest
# SPDX-FileCopyrightText: 2025 Jonas Huber <https://github.com/jh-RLI> © Reiner Lemoine Institut
#
# SPDX-License-Identifier: AGPL-3.0-or-later

# Copy the PostgreSQL JDBC driver into Ontop's lib directory
COPY serviceConfigs/ontop/postgresql.jar /opt/ontop/lib/postgresql.jar
# ── Stage 1: fetch the PostgreSQL JDBC driver ────────────────────────────────
# Downloaded at build time from Maven Central, so no jar needs to live in the
# build context (nothing to download or commit manually). Override the version
# with --build-arg JDBC_VERSION=x.y.z.
FROM docker.io/curlimages/curl:latest AS jdbc
ARG JDBC_VERSION=42.7.3
RUN curl -fsSL \
"https://repo1.maven.org/maven2/org/postgresql/postgresql/${JDBC_VERSION}/postgresql-${JDBC_VERSION}.jar" \
-o /tmp/postgresql.jar

# Ensure it's world-readable (optional but safe)
# RUN chmod 644 /opt/ontop/lib/postgresql.jar
# ── Stage 2: Ontop, fully self-provisioned ───────────────────────────────────
FROM docker.io/ontop/ontop:latest

# JDBC driver on Ontop's classpath (/opt/ontop/jdbc is searched by /opt/ontop/ontop).
COPY --from=jdbc /tmp/postgresql.jar /opt/ontop/jdbc/postgresql.jar

# Bake the ontology and an EMPTY default mapping into the image so the service
# starts with zero host files and works regardless of database state — an empty
# mapping references no tables, so the endpoint comes up cleanly even before the
# OEDB data tables exist. Provide the real mapping at runtime by bind-mounting a
# file over /opt/ontop-config/mapping.obda (see docker/serviceConfigs/ontop/
# mapping.obda) or by pointing ONTOP_MAPPING_FILE elsewhere.
COPY serviceConfigs/ontop/ontology.owl /opt/ontop-config/ontology.owl
COPY serviceConfigs/ontop/mapping.default.obda /opt/ontop-config/mapping.obda

# Non-secret configuration baked as defaults. The DB connection is supplied at
# runtime via environment variables (ONTOP_DB_URL / ONTOP_DB_USER /
# ONTOP_DB_PASSWORD) from oep.env / .env — Ontop reads them natively, so no
# ontop.properties file is needed. ONTOP_LAZY_INIT lets the endpoint start even
# before the mapped tables exist in the database.
ENV ONTOP_MAPPING_FILE=/opt/ontop-config/mapping.obda \
ONTOP_ONTOLOGY_FILE=/opt/ontop-config/ontology.owl \
ONTOP_DB_DRIVER=org.postgresql.Driver \
ONTOP_LAZY_INIT=true
10 changes: 8 additions & 2 deletions docker/docker-compose.dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,16 @@ services:
container_name: ontop
ports:
- "8080:8080"
# Ontology, mapping and JDBC driver are baked into the image. The dir is
# still mounted so you can live-edit mapping.obda / ontology.owl without a
# rebuild. DB connection comes from env — no ontop.properties needed.
environment:
ONTOP_MAPPING_FILE: "/opt/ontop-config/mapping.obda"
ONTOP_OWL_FILE: "/opt/ontop-config/ontology.owl"
ONTOP_PROPERTIES_FILE: "/opt/ontop-config/ontop.properties"
ONTOP_ONTOLOGY_FILE: "/opt/ontop-config/ontology.owl"
ONTOP_DB_URL: "jdbc:postgresql://postgres:5432/oedb"
ONTOP_DB_USER: postgres
ONTOP_DB_PASSWORD: postgres
ONTOP_WAIT_FOR: postgres:5432
volumes:
- ./serviceConfigs/ontop:/opt/ontop-config
depends_on:
Expand Down
12 changes: 9 additions & 3 deletions docker/serviceConfigs/ontop/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
# Complete ontop setup

Download the database JDBC driver for ontop:
The PostgreSQL JDBC driver is **downloaded automatically** when the ontop image
is built — `docker/Dockerfile.ontop` fetches it from Maven Central and places it
on Ontop's classpath. No manual download is required.

- <https://jdbc.postgresql.org/>
To use a different driver version, build with:

Add the file postgresql.jar to this directory.
```sh
podman build --build-arg JDBC_VERSION=42.7.3 \
-t ghcr.io/openenergyplatform/oeplatform-ontop:latest \
-f docker/Dockerfile.ontop docker/
```
16 changes: 16 additions & 0 deletions docker/serviceConfigs/ontop/mapping.default.obda
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[PrefixDeclaration]
: http://example.org/voc#
owl: http://www.w3.org/2002/07/owl#
rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns#
xml: http://www.w3.org/XML/1998/namespace
xsd: http://www.w3.org/2001/XMLSchema#
foaf: http://xmlns.com/foaf/0.1/
obda: https://w3id.org/obda/vocabulary#
rdfs: http://www.w3.org/2000/01/rdf-schema#
oeo: https://openenergyplatform.org/ontology/oeo/
oekg: https://openenergyplatform.org/ontology/oeo/oekg/
llc: https://www.omg.org/spec/LCC/Countries/ISO3166-1-CountryCodes/

[MappingDeclaration] @collection [[

]]
22 changes: 15 additions & 7 deletions docs/installation/guides/setup-ontop.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,22 @@ mappings ontop on the "normal" sql like table definition.

We offer the pre-configured ontop service as part of the OEP-docker setup for
development. It comes with a empty semantic mapping template which can be
extended based on the user needs. You still need to download the JDBC database
driver to enable connection to the postgresql database OEDB.
extended based on the user needs.

Once you downloaded the driver make sure it is available in the ontop config
directory and only then build the ontop service using docker.
The ontop image is self-provisioning — the PostgreSQL JDBC driver, the ontology
(`ontology.owl`) and the mapping (`mapping.obda`) are all baked into the image
at build time. No files need to be placed or downloaded manually.

Download the database JDBC driver for ontop:
- The JDBC driver is fetched from Maven Central during the build. Pin a version
with `--build-arg JDBC_VERSION=x.y.z` (default: 42.7.3).
- The database connection is configured through environment variables (in
`oep.env` / `.env`), so there is no `ontop.properties` file to create:

- <https://jdbc.postgresql.org/>
```sh
ONTOP_DB_URL=jdbc:postgresql://postgres:5432/oedb
ONTOP_DB_USER=<postgres user>
ONTOP_DB_PASSWORD=<postgres password>
```

Add the file postgresql.jar to this directory.
To customise the mapping, edit `docker/serviceConfigs/ontop/mapping.obda` and
rebuild, or bind-mount your own file over `/opt/ontop-config/mapping.obda`.
18 changes: 13 additions & 5 deletions oeplatform/securitysettings.py.default
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ from pathlib import Path
SECRET_KEY = '0'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
# Set OEP_DEBUG=False on production servers.
DEBUG = os.environ.get("OEP_DEBUG", "True").strip().lower() in ("true", "1", "yes")

# Runs on localhost only
URL = '127.0.0.1'
# Public host the platform is served under (without scheme).
# On localhost this stays 127.0.0.1; in production set OEP_URL to your domain.
URL = os.environ.get("OEP_URL", "127.0.0.1")

USE_DOCKER = True

Expand All @@ -25,8 +27,14 @@ DATABASES = {
}


# This is unnecessary as long DEBUG is True
ALLOWED_HOSTS = [] if DEBUG else ['localhost']
# Comma-separated list of hosts/domains the site may serve, e.g.
# OEP_ALLOWED_HOSTS="openenergyplatform.org,www.openenergyplatform.org".
# When DEBUG is on this can stay empty; in production it must contain your domain.
_allowed_hosts = os.environ.get("OEP_ALLOWED_HOSTS", "").strip()
if _allowed_hosts:
ALLOWED_HOSTS = [h.strip() for h in _allowed_hosts.split(",") if h.strip()]
else:
ALLOWED_HOSTS = [] if DEBUG else ['localhost']

TIME_OUT = 30
USER_CONNECTION_LIMIT = 4
Expand Down
31 changes: 31 additions & 0 deletions oeplatform/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
SPDX-License-Identifier: AGPL-3.0-or-later
""" # noqa: 501

import os
import sys

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
Expand Down Expand Up @@ -121,6 +122,36 @@
]


# ── Reverse proxy / HTTPS ─────────────────────────────────────────────────────
# When the platform runs behind a TLS-terminating reverse proxy (e.g. nginx on
# the production server), the proxy speaks HTTPS to the client and plain HTTP to
# the container. These settings let Django recognise the original HTTPS request.
# Enable by setting OEP_BEHIND_TLS_PROXY=True on the server.
if os.environ.get("OEP_BEHIND_TLS_PROXY", "False").strip().lower() in (
"true",
"1",
"yes",
):
# The proxy must send this header
# (nginx: proxy_set_header X-Forwarded-Proto $scheme;)
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True

# Comma-separated list of trusted origins for CSRF checks under HTTPS, e.g.
# OEP_CSRF_TRUSTED_ORIGINS="https://openenergyplatform.org,www.openenergyplatform.org".
# Required by Django for unsafe (POST/PUT/…) requests served over HTTPS. Django
# 4+ requires each origin to include a scheme, so a bare host (e.g. "example.org")
# is normalised to "https://example.org".
_csrf_trusted_origins = os.environ.get("OEP_CSRF_TRUSTED_ORIGINS", "").strip()
if _csrf_trusted_origins:
CSRF_TRUSTED_ORIGINS = [
origin if "://" in origin else f"https://{origin}"
for origin in (o.strip() for o in _csrf_trusted_origins.split(","))
if origin
]


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/

Expand Down
43 changes: 43 additions & 0 deletions podman/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# SPDX-FileCopyrightText: 2025 Jonas Huber <https://github.com/jh-RLI> © Reiner Lemoine Institut
#
# SPDX-License-Identifier: AGPL-3.0-or-later

# Copy this file to .env on the server and fill in all values before starting
# the stack. The .env file must never be committed to version control.
#
# Usage:
# cp podman/.env.example .env
# # edit .env with real values
# podman-compose --env-file .env -f podman/podman-compose.yaml up -d

# ── PostgreSQL ────────────────────────────────────────────────────────────────
POSTGRES_USER=
POSTGRES_PASSWORD=

# ── OEPlatform app ────────────────────────────────────────────────────────────
# Database credentials passed to Django (must match the PostgreSQL values above)
OEP_DJANGO_USER=
OEP_DB_PW=
OEP_DJANGO_HOST=postgres
OEP_DJANGO_NAME=oep_django
LOCAL_DB_USER=
LOCAL_DB_PASSWORD=
LOCAL_DB_NAME=oedb
LOCAL_DB_HOST=postgres

# ── Fuseki ────────────────────────────────────────────────────────────────────
FUSEKI_ADMIN_PASSWORD=
FUSEKI_DATASET_1=ds

# ── Ontop SPARQL endpoint ─────────────────────────────────────────────────────
# Ontology, mapping and JDBC driver are baked into the image; only the DB
# connection is configured. USER/PASSWORD default to the POSTGRES_* values above
# via the compose file — override the URL here if the DB is elsewhere.
# ONTOP_DB_URL=jdbc:postgresql://postgres:5432/oedb

# ── Ports (optional — defaults shown) ────────────────────────────────────────
# OEP_PORT_WEB=8080
# OEP_PORT_POSTGRES=5432
# OEP_PORT_FUSEKI=3030
# OEP_PORT_ONTOP=8081
# OEP_PORT_LOOKUP=3004
Loading
Loading