Skip to content

Production deployment (Podman) โ€” Overview & architecture#

You are here

Start of the Production deployment (Podman) guide. This page explains what the stack is and how the pieces fit together. When you understand the architecture, continue to Install.

This guide describes the production deployment of the Open Energy Platform using rootless Podman with systemd Quadlets. It is the canonical, supported production path.

Not the development setup

The docker / podman-compose paths documented elsewhere in these docs are for development and CI. For a real server, follow this section.

The stack at a glance#

  • The application runs as a set of rootless Podman containers owned by an unprivileged service user, managed as systemd --user Quadlets โ€” one systemd unit per service, with linger enabled so they survive logout and reboot.
  • A root nginx on the host terminates TLS on port 443 and reverse-proxies plain HTTP to the app container (which listens only on 127.0.0.1). Rootless containers cannot bind privileged ports, so nginx (running as root) owns :443.
  • All services share one oep Podman network (netavark + aardvark-dns), so they reach each other by container name (postgres, fuseki, ontop, โ€ฆ).
  • Persistent data lives in named volumes, physically under the service user's home directory. Where that home lives โ€” and therefore where the data physically sits โ€” is a per-server storage decision, not part of this generic guide.
  • One config file drives everything: ~/.config/oeplatform/oep.env is read by every container unit, and nginx reads the public domain from it too.

Services#

Service Role
oeplatform The Django web application (Apache2 + mod_wsgi). Serves the platform.
postgres PostgreSQL: the OEDB (oedb) data tables + the Django DB (oep_django).
fuseki Apache Jena Fuseki triple store (the OEKG / SPARQL knowledge graph).
ontop A SPARQL-over-SQL endpoint (Virtual Knowledge Graph) exposing OEDB tables as RDF via a semantic mapping.
lookup Term autocomplete / lookup โ€” see below.
nginx Host package (not a container): terminates backend TLS on :443 and proxies to the app.

The lookup service#

The lookup service runs DBpedia Lookup, a generic lookup engine over any SPARQL-compatible source. Rather than querying a SPARQL endpoint live on every request, it indexes the results of configured queries into a local index and then serves fast prefix / keyword lookups against that index. In OEP it is used to autocomplete ontology terms (e.g. when annotating metadata). Its behaviour is driven by two kinds of config:

  • a service config (config.yaml) โ€” the searchable fields and match/boost tuning, and
  • one or more indexers โ€” each names a sparqlEndpoint and the SPARQL queries that populate the index fields.

The indexer wiring (which endpoint is indexed, and how) is covered as a config point in Install.

Request & TLS flow#

The public entry point is typically an upstream reverse proxy (managed by your institution's IT) that terminates the public TLS certificate, authenticates, and then re-encrypts to this host over HTTPS. That means TLS is terminated twice: once at the upstream proxy (public cert) and once at the host nginx (a backend cert, which may be self-signed โ€” the upstream proxy typically does not verify the backend cert).

sequenceDiagram
  participant C as Browser
  participant A as Upstream proxy (:443)
  participant N as host nginx (:443)
  participant O as oeplatform (:8080โ†’:80)
  C->>A: HTTPS (+ credentials, if the proxy requires auth)
  A->>A: terminate public TLS, authenticate
  A->>N: HTTPS (re-encrypt), X-Forwarded-Proto: https
  N->>N: terminate backend TLS
  N->>O: HTTP, forwards Host + X-Forwarded-Proto
  O->>O: Django (OEP_BEHIND_TLS_PROXY) treats request as HTTPS
  O-->>C: response (back up the chain)

If you have no upstream proxy

The host nginx can also face the public directly. In that case you terminate a real (e.g. Let's Encrypt) certificate at nginx instead of a self-signed backend cert. This guide assumes the upstream-proxy layout; adapt the certificate accordingly.

Topology#

flowchart TB
  user["Browser<br/>https://your-domain.org"]

  subgraph up["Upstream network โ€” managed by IT"]
    proxy["Upstream reverse proxy<br/>:443 ยท public TLS + auth<br/>re-encrypts to backend"]
  end

  subgraph host["Deployment host<br/>rootless Podman ยท service user ยท systemd --user + linger"]
    nginx["nginx ยท system service (root)<br/>:443 TLS<br/>proxy_pass โ†’ 127.0.0.1:8080"]

    subgraph net["Podman network 'oep' ยท netavark + aardvark-dns"]
      app["oeplatform<br/>127.0.0.1:8080 โ†’ :80<br/>Apache2 + mod_wsgi + Django"]
      pg[("postgres<br/>:5432<br/>oedb + oep_django")]
      fuseki["fuseki<br/>:3030<br/>OEKG triple store"]
      ontop["ontop<br/>:8081 โ†’ :8080<br/>SPARQL VKG"]
      lookup["lookup<br/>:3004 โ†’ :8082<br/>autocomplete"]
    end

    subgraph vols["Named volumes โ€” under the service user's home"]
      v1[("pgdata")]
      v2[("oeplatform-ontologies")]
      v3[("oeplatform-media")]
      v4[("fuseki-databases")]
    end
  end

  user -->|"HTTPS :443"| proxy
  proxy -->|"HTTPS :443 (re-encrypt)"| nginx
  nginx -->|"HTTP"| app

  app -->|"SQL"| pg
  app -->|"SPARQL"| fuseki
  app -->|"SPARQL"| ontop
  app -->|"lookup API"| lookup
  ontop -->|"JDBC โ†’ oedb"| pg

  pg -.-> v1
  app -.-> v2
  app -.-> v3
  fuseki -.-> v4

Design principles worth knowing#

  • Self-provisioning images. The app image bakes in the OEO ontology release and the OEO-extended template and seeds them into volumes on first start. The ontop image bakes in its JDBC driver, ontology and a (safe, empty) mapping. You do not place these files by hand.
  • Config is environment, not files. Credentials, the public host, and HTTPS behaviour all come from oep.env. There are no per-service config files to hand-edit for a normal deployment (the two exceptions โ€” the lookup config and a real ontop mapping โ€” are called out where relevant).
  • systemd owns the lifecycle. Start-on-boot, restart-on-failure, dependency ordering and logs all go through systemctl --user / journalctl --user.

OEP-internal specifics live elsewhere

This guide is generic and uses placeholders (your-domain.org, <db-password>, โ€ฆ). The concrete values, hostnames and server paths for the OEP team's own servers are maintained separately (internal runbook), not in this public documentation.


Next โ†’ Install โ€” bring the whole stack up on a fresh host.