.. _installation: Installation ================ .. note:: If you don't already have a license, you can request a license key at https://open-fhir.com#access. License will be provided automatically to you via email. Docker ------------- openFHIR can be easily installed using Docker. The official Docker image is available at `openfhir/openfhir-enterprise:latest` (`build` tag is also available). To run Open-FHIR using Docker, use the following command: .. code-block:: bash docker run -e SPRING_DATA_MONGODB_URI=mongodb://your-mongodb-url \ -e OPENFHIR_DB_TYPE=mongo \ -e OPENFHIR_LICENSE=/path/to/license \ -p 8080:8080 openfhir/openfhir-enterprise:latest Database -------- At the moment, MongoDB and PostgreSQL are supported. There is no default: set ``openfhir.db.type`` (``OPENFHIR_DB_TYPE``) to ``mongo`` or ``postgres``, otherwise the engine does not start (see `Startup checks`_). MongoDB ####### ``-e OPENFHIR_DB_TYPE=mongo`` or in docker compose .. code-block:: bash services: openfhir: .... environment: SPRING_DATA_MONGODB_URI: "mongodb://openfhir:openfhir@mongodb:27017/openfhir" OPENFHIR_DB_TYPE: "mongo" .. _postgres-section: Postgres ######## ``-e OPENFHIR_DB_TYPE=postgres`` or in docker compose .. code-block:: bash services: openfhir: .... environment: SPRING_DATASOURCE_URL: jdbc:postgresql://localhost:5432/openfhir SPRING_DATASOURCE_USERNAME: postgres SPRING_DATASOURCE_PASSWORD: postgres OPENFHIR_DB_TYPE: "postgres" Connections to PostgreSQL are pooled (HikariCP, pool ``openfhir-postgres``). The pool is tuned with the standard Spring Boot ``spring.datasource.hikari.*`` properties, e.g. ``SPRING_DATASOURCE_HIKARI_MAXIMUM_POOL_SIZE`` (default ``10``) or ``SPRING_DATASOURCE_HIKARI_CONNECTION_TIMEOUT`` (milliseconds, default ``30000``). Every node keeps its own pool, so in a multi-node deployment (see :doc:`ha`) the number of nodes times the maximum pool size must stay below the server's ``max_connections`` (``100`` by default in PostgreSQL), leaving room for other clients. Database stores the following state: - `bootstrapEntity`: ledger of all files that were bootstrapped, recording each file's path, a hash of the content that was applied and the entity it created, so that a re-run can tell an unchanged file (skipped) from a changed one (updated in place) and a new one (created). See more: :ref:`state-configuration` - `conceptMapFhirEntity`: concept maps for terminology (see more: :doc:`terminology`) - `fhirConnectContextEntity`: FHIR Connect context mappers - `fhirConnectMapperEntity`: FHIR Connect model mappers - `optEntity`: Operational templates (unless the engine is integrated to an external openEHR repository, see more: :doc:`integrations`) Example docker compose - postgres ------------------------- :: services: openfhir: image: openfhir/openfhir-enterprise:latest restart: always network_mode: host container_name: openfhir ports: - "8080:8080" volumes: - "/opt/openfhir/config/examples/:/home/" - "/opt/openfhir/config/license.json:/app/openfhir-license.json" - "/opt/openfhir/config/bootstrap/:/app/bootstrap/" - "/opt/openfhir/config/application.yaml:/application.yaml" postgres: image: postgres:16 network_mode: host ports: - "5432:5432" environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres volumes: - "/opt/openfhir/config/init-db.sql:/docker-entrypoint-initdb.d/init-db.sql" init-db.sql :: -- Create OpenFHIR database and user CREATE DATABASE openfhir; CREATE USER openfhir WITH ENCRYPTED PASSWORD 'openfhir'; GRANT ALL PRIVILEGES ON DATABASE openfhir TO openfhir; -- Ensure the users can connect and create objects within their databases ALTER DATABASE openfhir OWNER TO openfhir; -- Grant privileges to schemas (optional but recommended) ALTER ROLE openfhir SET search_path TO public; application.yaml :: spring: datasource: url: jdbc:postgresql://localhost:5432/openfhir username: openfhir password: openfhir server: error: include-stacktrace: never include-message: always port: 8080 openfhir: db: type: postgres license: /app/openfhir-license.json bootstrap: dir: /app/bootstrap recursively-open-directories: true Example docker compose - mongo ---------------------- Example docker compose file :: services: openfhir: image: openfhir/openfhir-enterprise:latest container_name: openfhir ports: - "8080:8080" volumes: - "/home/user/Documents/openFHIR/license.json:/app/openfhir-license.json" - "/home/user/Documents/openFHIR/openfhir-bootstrap/:/app/bootstrap/" environment: SPRING_DATA_MONGODB_URI: "mongodb://openfhir:openfhir@mongodb:27017/openfhir" OPENFHIR_DB_TYPE: "mongo" OPENFHIR_LICENSE: "/app/openfhir-license.json" # could also be left out, as this is the default OPENFHIR_BOOTSTRAP_DIR: "/app/bootstrap/" # could also be left out, as this is the default mongodb: image: mongo:latest container_name: mongodb ports: - "27017:27017" environment: MONGO_INITDB_ROOT_USERNAME: "admin" MONGO_INITDB_ROOT_PASSWORD: "admin" MONGO_INITDB_DATABASE: "openfhir" volumes: - ./init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js:ro init-mongo.js :: // Connect to the admin database to create a user for a specific database db = db.getSiblingDB('openfhir'); // Create a new user with readWrite access to the 'openfhir' database db.createUser({ user: 'openfhir', pwd: 'openfhir', roles: [ { role: 'readWrite', db: 'openfhir' } ] }); Startup checks -------------- The engine checks its license and database configuration at startup. When something is wrong it stops and logs an ``APPLICATION FAILED TO START`` block that says what is wrong and what to do about it, for example: :: *************************** APPLICATION FAILED TO START *************************** Description: openFHIR license file not found at '/app/openfhir-license.json'. Action: Place your license file at that path or point 'openfhir.license' to where it is (relative paths are resolved against the working directory). To obtain a license, visit https://open-fhir.com or contact us at info@open-fhir.com. This is reported for: * a license that is not configured, cannot be found or read, is not a license file, has been modified or has expired (``OPENFHIR_LICENSE``) * an ``OPENFHIR_DB_TYPE`` other than ``mongo`` or ``postgres`` * missing database connection settings: ``SPRING_DATA_MONGODB_URI`` for ``mongo``; ``SPRING_DATASOURCE_URL``, ``SPRING_DATASOURCE_USERNAME`` and ``SPRING_DATASOURCE_PASSWORD`` (which may be empty) for ``postgres`` * a database that cannot be reached, whose host does not resolve, that rejects the credentials or the connection, (MongoDB) whose TLS handshake fails or (PostgreSQL) whose database does not exist. Hosts are named, credentials never are * invalid queue configuration, or queue support enabled without the ``messaging`` license option (see :doc:`messaging`) The full stack trace of the failure is logged at ``DEBUG`` level (``LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_BOOT_DIAGNOSTICS=DEBUG``). License expiry -------------- The license file is validated again once a day. Its expiry date and options are logged at startup and with every check; from 30 days before expiry this is a ``WARN``. ``GET /status`` reports them under ``license`` (see :doc:`restful_apis`). To renew, overwrite the file at ``OPENFHIR_LICENSE`` with the new license before the old one expires (e.g. ``cp new-license.json license.json``); the daily check picks it up without a restart, including options the new license adds or removes (e.g. ``terminology``). The ``messaging`` option is only checked at startup, so queue support is switched on or off by a new license only after a restart. .. note:: When the license is mounted as a single file, as in the Docker examples above, the container does not see a file that is replaced rather than overwritten (``mv``, or an editor that saves by renaming), so overwrite it in place. Alternatively, mount a directory of its own and point ``OPENFHIR_LICENSE`` into it, e.g. ``-v /opt/openfhir/license:/app/license:ro`` with ``OPENFHIR_LICENSE=/app/license/openfhir-license.json``. Do not mount over ``/app`` itself, which holds the engine. A Kubernetes Secret or ConfigMap only reaches a running container when it is mounted as a directory, not with ``subPath``; otherwise restart the engine after renewing. When the check finds the license no longer valid, it logs what is wrong and what to do about it, and checks again 30 seconds later, so a file that is being overwritten or is briefly unavailable does not stop the engine. If the license is still not valid, the engine shuts down gracefully (in-flight requests, queue consumers and cluster membership are closed cleanly) and exits with code ``1``. If that does not finish within 60 seconds, the process is halted. With a restart policy, the engine then fails to start again with an ``APPLICATION FAILED TO START`` message until a valid license is in place.