# Setup Portal Linux

## Requirements

See the [Requirements](/UDiTH%20Portal/Requirements) page for the Portal (Linux) requirements.

## Installation

We recommend using a guided installation with CAXperts for UDiTH Portal. Please contact `support@caxperts.com` for information.

The Docker images are available at `quay.io/caxperts/portal` and `quay.io/caxperts/caxturn`.

### Reverse Proxy

Although it is possible to expose Portal directly through HTTPS, this is not recommended.

Please use a reverse proxy in front of Portal.

### Installation Steps

- Ensure that all requirements are met.
- Ensure that MSSQL Server and Keycloak are accessible from the new Portal container.
- Create a container for Portal. A template `docker-compose.yml` file with the most important settings is included below.
  - In comparison with the Windows version, the Docker variant mainly uses environment variables to configure the instance.

### Sample `docker-compose.yml` File

```yml
services:
  portal_demo:
    image: quay.io/caxperts/portal:${PORTAL_VERSION:-latest}
    restart: unless-stopped
    environment:
      - KeycloakAdmin__Name=${KEYCLOAK_ADMIN_USER}
      - KeycloakAdmin__Password_file=${KEYCLOAK_ADMIN_PASSWORD}
      - KeycloakUrl=https://${KEYCLOAK_DOMAIN}${KEYCLOAK_PATH:-/}
      - KeycloakRealm=${KEYCLOAK_REALM}
      - ConnectionStrings__AdminConnection=${PORTAL_CONNECTION_STRING}
      - ServerUrl=https://${PORTAL_DOMAIN}
      - LicenseKey_file=${PORTAL_LICENSEKEY}
    networks:
      - sqlserver
      - external
    volumes:
      - ${PORTAL_IMPORT_FOLDER}:/import
      - ${PORTAL_STORAGE_FOLDER}:/storage
networks:
  external:
    external: true
    name: external
  sqlserver:
    external: true
    name: sqlserver
```

### Sample `docker-compose.yml` File with CAX Turn for BBV

```yml
services:
  portal_demo:
    image: quay.io/caxperts/portal:${PORTAL_VERSION:-latest}
    restart: unless-stopped
    environment:
      - KeycloakAdmin__Name=${KEYCLOAK_ADMIN_USER}
      - KeycloakAdmin__Password_file=${KEYCLOAK_ADMIN_PASSWORD}
      - KeycloakUrl=https://${KEYCLOAK_DOMAIN}${KEYCLOAK_PATH:-/}
      - KeycloakRealm=${KEYCLOAK_REALM}
      - ConnectionStrings__AdminConnection=${PORTAL_CONNECTION_STRING}
      - ServerUrl=https://${PORTAL_DOMAIN}
      - StreamingConfig__IceConfig__IceServers__0__Urls__0=turn:${TURNSERVER_ADDRESS}:${TURNSERVER_UDP_PORT}
      - StreamingConfig__IceConfig__IceServers__0__Urls__1=turn:${TURNSERVER_ADDRESS}:${TURNSERVER_TCP_PORT}?transport=tcp
      - StreamingConfig__IceConfig__IceServers__0__CredentialType=password
      - StreamingConfig__IceConfig__IceServers__0__Username=${TURNSERVER_USERNAME}
      - StreamingConfig__IceConfig__IceServers__0__Credential=${TURNSERVER_PASSWORD}
      - LicenseKey_file=${PORTAL_LICENSEKEY}
    networks:
      - sqlserver
      - external
    volumes:
      - ${PORTAL_IMPORT_FOLDER}:/import
      - ${PORTAL_STORAGE_FOLDER}:/storage
  turn:
    image: quay.io/caxperts/caxturn:${CAXTURN_VERSION:-latest}
    # Port range mapping is very slow, so network_mode: host is used.
    network_mode: host
    environment:
      - CAXTURN_ENABLE_UDP=true
      - CAXTURN_ENABLE_TCP=true
      - CAXTURN_UDP_PORT=${CAXTURN_UDP_PORT}
      - CAXTURN_TCP_PORT=${CAXTURN_TCP_PORT}

      - CAXTURN_MIN_PORT=${CAXTURN_MIN_PORT}
      - CAXTURN_MAX_PORT=${CAXTURN_MAX_PORT}

      - CAXTURN_PUBLIC_IP=${CAXTURN_PUBLIC_IP}
      - CAXTURN_USERNAME=${CAXTURN_USERNAME}
      - CAXTURN_PASSWORD=${CAXTURN_PASSWORD}
    restart: unless-stopped
networks:
  external:
    external: true
    name: external
  sqlserver:
    external: true
    name: sqlserver
```

### Sample `docker-compose.yml` for Keycloak

```yml
services:
  keycloak:
    image: quay.io/keycloak/keycloak:${KEYCLOAK_VERSION:-26.6.1}
    restart: unless-stopped
    environment:
      # >= 26
      KC_BOOTSTRAP_ADMIN_USERNAME: ${KEYCLOAK_ADMIN_USER}
      KC_BOOTSTRAP_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD}
      # <26
      KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN_USER}
      KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD}
      KC_DB: mssql
      KC_DB_URL: "jdbc:sqlserver://${KEYCLOAK_SQLSERVER_ADDRESS:-mssql:1433};databaseName=${KEYCLOAK_SQLSERVER_DATABASENAME};encrypt=true;trustServerCertificate=true;"
      KC_DB_USERNAME: ${KEYCLOAK_SQLSERVER_USER}
      KC_DB_PASSWORD: ${KEYCLOAK_SQLSERVER_PASSWORD}
      KC_HEALTH_ENABLED: "true"
      KC_METRICS_ENABLED: "true"
      KC_HTTP_ENABLED: true
      KC_PROXY_HEADERS: xforwarded
      KC_HOSTNAME: https://${KEYCLOAK_DOMAIN}${KEYCLOAK_PATH:-/}
      KC_HTTP_RELATIVE_PATH: ${KEYCLOAK_RELATIVE_PATH:-/}
    command: start
    networks:
      - "sqlserver"
      - "external"
    healthcheck:
      test: ['CMD-SHELL', '[ -f /tmp/HealthCheck.java ] || echo "public class HealthCheck { public static void main(String[] args) throws java.lang.Throwable { System.exit(java.net.HttpURLConnection.HTTP_OK == ((java.net.HttpURLConnection)new java.net.URL(args[0]).openConnection()).getResponseCode() ? 0 : 1); } }" > /tmp/HealthCheck.java && java /tmp/HealthCheck.java http://localhost:9000${KEYCLOAK_RELATIVE_PATH:-/}/health/live']
      interval: 5s
      timeout: 3s
      retries: 3
      start_period: 1m
    volumes: 
      - /root/storage/keycloak/providers:/opt/keycloak/providers
networks:
  external:
    external: true
    name: "external"
  sqlserver:
    external: true
    name: "sqlserver"
```

### Sample `docker-compose.yml` for MSSQL

```yml
services:
 mssql:
    image: mcr.microsoft.com/mssql/server:${MSSQL_VERSION:-2025-latest}
    environment:
      SA_PASSWORD: ${SQLSERVER_SA_PASSWORD}
      ACCEPT_EULA: "Y"
    volumes:
      - ${SQLSERVER_STORAGE_FOLDER}:/var/opt/mssql
    ports:
      - 1433:1433 # Not required, but useful for remote connections. Secure this appropriately.
    restart: unless-stopped
    healthcheck:
      # -C disable checks for encryption
      test: /opt/mssql-tools18/bin/sqlcmd -S localhost -C -U sa -P "$SQLSERVER_SA_PASSWORD" -Q "SELECT 1" -b -o /dev/null
      interval: 5s
      timeout: 3s
      retries: 3
      start_period: 10m
networks:
  default:
    external: true
    name: sqlserver
```

## Accessing Portal

Go to the Keycloak installation and update the `defaultadmin` password in the realm you specified.

Then go to `https://<alias/(sub)domain>/Portal` and sign in by using `defaultadmin` and the password you defined.

You can add additional users through the web interface or connect the portal to an SSO provider.

![Keycloak Login](media/Portal_Login.png)

## Configuration

In comparison with the Windows version, the Docker variant is configured mainly through environment variables, optionally backed by Docker secret files.

For the complete list of available settings, and details on how to provide them on both Linux and Windows, see the [Portal Configuration](/UDiTH%20Portal/Portal%20Configuration) page.

## Security

As with any web server, it is recommended to keep it updated with the latest security fixes.

