# Quickstart

Three ways to run HelixIAM `v1.1.0-rc.14`: pick one. Each needs a HelixIAM licence: you receive registry
credentials and the release files from KubeDNA B.V. ([contact@helixiam.com](mailto:contact@helixiam.com)). The
commands below expect the registry user name and token in `REGISTRY_USER` and `REGISTRY_TOKEN`.

| | What you need | Time to first login | For |
| --- | --- | --- | --- |
| **Docker** | Docker with Compose v2, the compose file and registry credentials from your licence | about 2 minutes | a first look on your machine (trial) |
| **Kubernetes** | a cluster, `kubectl`, Helm 3.8+, registry credentials from your licence | about 5 minutes | trying the Helm chart on a cluster |
| **Local machine** | the server jar and registry credentials from your licence, Java 25, PostgreSQL, Docker for the console | about 5 minutes | development against the server |

=== "Docker"

    !!! info "Requires a HelixIAM licence"
        You receive registry credentials and the release files from KubeDNA B.V. No licence yet? Contact
        [contact@helixiam.com](mailto:contact@helixiam.com).

    **You need** Docker with Compose v2 (`docker compose version`), the `docker-compose.yml` provided with your
    licence (pinned to `v1.1.0-rc.14`) and your registry credentials.

    **1. Sign in to the registry and start.** The compose file runs PostgreSQL, Mailpit, the server and the admin
    console from the signed release images. Sessions are kept in PostgreSQL, so there is no Redis. Put the compose
    file in an empty directory, then:

    ```bash
    echo "$REGISTRY_TOKEN" | docker login ghcr.io -u "$REGISTRY_USER" --password-stdin
    docker compose up -d
    ```

    **2. Wait until it is ready.** The first start creates the database schema and the `master` realm. Either watch
    `docker compose ps` until `server` reports `healthy`, or wait for the readiness endpoint:

    ```bash
    until curl -fsS http://localhost:8080/actuator/health/readiness; do sleep 5; done
    ```

    **3. Open the admin console** at <http://localhost:8090> and sign in as `admin` / `admin` (or the
    `HELIX_ADMIN_PASSWORD` you set, see below).

    The server answers on <http://localhost:8080>. For example, the OIDC discovery document:

    ```bash
    curl -s http://localhost:8080/realms/master/.well-known/openid-configuration | jq '{issuer, device_authorization_endpoint}'
    ```
    ```json
    {
      "issuer": "http://localhost:8080/realms/master",
      "device_authorization_endpoint": "http://localhost:8080/realms/master/oauth2/device_authorization"
    }
    ```

    **Mailpit** at <http://localhost:8025> shows every email the server sends (verification, one-time codes,
    magic links).

    **Your own settings.** Put them in a `.env` file next to `docker-compose.yml`, **before the first start**:

    | Key | Default | Meaning |
    | --- | --- | --- |
    | `HELIX_ADMIN_PASSWORD` | `admin` | the bootstrap admin's password |
    | `DB_ENCRYPTION` | a demo key | the key (32 hex characters) that encrypts secrets at rest |
    | `HELIX_VERSION` | the release the file came with | the image tag of the server and the console |

    ```bash
    echo "HELIX_ADMIN_PASSWORD=$(openssl rand -base64 18)" >> .env
    echo "DB_ENCRYPTION=$(openssl rand -hex 16)" >> .env
    cat .env
    ```

    The admin and the encryption key take effect **on the first start only**, when the database is empty. Changing
    `HELIX_ADMIN_PASSWORD` later does not change the admin's password (change it in the console), and changing
    `DB_ENCRYPTION` later makes the stored secrets unreadable. To start over with new values, delete the data first
    with `docker compose down -v`.

    **Pin a version.** Set `HELIX_VERSION` to a release tag, then pull and restart:

    ```bash
    echo "HELIX_VERSION=v1.1.0-rc.14" >> .env
    docker compose pull && docker compose up -d
    ```

    **Stop and clean up.**

    ```bash
    docker compose down      # stop; the database is kept for the next start
    docker compose down -v   # stop and delete the data (database included)
    ```

    !!! warning "Trial only"
        This setup uses a published demo encryption key unless you set your own, a database password written in
        the compose file, plain http, cookies without the `Secure` flag and no second factor. Never use it for real
        users. For a real deployment use the Helm chart with your own
        secrets and https: see [Install](install.md).

=== "Kubernetes"

    !!! info "Requires a HelixIAM licence"
        You receive registry credentials and the release files from KubeDNA B.V. No licence yet? Contact
        [contact@helixiam.com](mailto:contact@helixiam.com).

    **You need** a Kubernetes cluster (1.25 or newer; a local one such as k3d, kind or minikube works), `kubectl`,
    Helm 3.8 or newer (for OCI charts) and your registry credentials. Everything goes into its own namespace, so
    cleaning up is one command. Sign Helm in to the registry, and give the cluster a pull secret for the images:

    ```bash
    echo "$REGISTRY_TOKEN" | helm registry login ghcr.io -u "$REGISTRY_USER" --password-stdin
    kubectl create namespace helixiam
    kubectl -n helixiam create secret docker-registry helixiam-registry \
      --docker-server=ghcr.io --docker-username="$REGISTRY_USER" --docker-password="$REGISTRY_TOKEN"
    ```

    **1. The Secret.** The chart never inlines secrets. It reads four keys from a Secret you create: the database
    user and password, the encryption key (16 bytes as hex; keep it for the life of the install) and the bootstrap
    admin password. This generates them:

    ```bash
    kubectl -n helixiam create secret generic helixiam-secrets \
      --from-literal=db-username=helix \
      --from-literal=db-password="$(openssl rand -hex 16)" \
      --from-literal=db-encryption="$(openssl rand -hex 16)" \
      --from-literal=admin-password="$(openssl rand -base64 18)"
    ```

    **2. A throwaway PostgreSQL.** The chart does not bundle a database. To try it, run one that reads its user and
    password from the same Secret:

    !!! danger "Not for production"
        One pod, no persistent volume: the data is lost when the pod restarts. In production, point
        `database.host` at a managed or replicated PostgreSQL, with TLS (`database.sslMode`).

    ```bash
    kubectl -n helixiam apply -f - <<'EOF'
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: postgres
    spec:
      replicas: 1
      selector:
        matchLabels: {app: postgres}
      template:
        metadata:
          labels: {app: postgres}
        spec:
          containers:
            - name: postgres
              image: postgres:16-alpine
              env:
                - name: POSTGRES_DB
                  value: helixiam
                - name: POSTGRES_USER
                  valueFrom: {secretKeyRef: {name: helixiam-secrets, key: db-username}}
                - name: POSTGRES_PASSWORD
                  valueFrom: {secretKeyRef: {name: helixiam-secrets, key: db-password}}
              ports:
                - containerPort: 5432
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: postgres
    spec:
      selector: {app: postgres}
      ports:
        - port: 5432
    EOF
    kubectl -n helixiam rollout status deploy/postgres
    ```

    **3. Install the chart** from the OCI registry, with the admin console on and sessions in PostgreSQL (no Redis):

    ```bash
    helm install helixiam oci://ghcr.io/melmarzgioui/helixiam/charts/helixiam --version 1.1.0-rc.14 \
      --namespace helixiam \
      --set database.host=postgres \
      --set secrets.existingSecret=helixiam-secrets \
      --set 'imagePullSecrets[0].name=helixiam-registry' \
      --set config.sessionStore=queue \
      --set console.enabled=true \
      --set config.idpBaseUrl=http://localhost:8080 \
      --set config.spBaseUrl=http://localhost:8090 \
      --set config.cookieSecure=false \
      --set replicaCount=1
    kubectl -n helixiam rollout status deploy/helixiam --timeout=5m
    kubectl -n helixiam rollout status deploy/helixiam-console
    ```

    The last two `--set` lines are for this trial over `http://localhost` only: cookies without the `Secure` flag
    (the chart's default `config.cookieSecure=true` is for https) and one server replica (default `replicaCount=2`).

    The two base URLs are the addresses of the port-forwards below. The server answers only the hosts of
    `config.idpBaseUrl` and `config.spBaseUrl`, and the console passes on the browser's `Host` (`localhost:8090`),
    so `config.allowedHosts` needs nothing extra here. Add names to it only when something calls the server on
    another host, such as an in-cluster relying party on `helixiam.helixiam.svc`.

    **4. Port-forward** the server and the console, each in its own terminal:

    ```bash
    kubectl -n helixiam port-forward svc/helixiam 8080:8080
    ```
    ```bash
    kubectl -n helixiam port-forward svc/helixiam-console 8090:8080
    ```

    If the server pod restarts (for example after `helm upgrade`), run the port-forward again.

    The application answers on port 8080. Liveness, readiness and `/actuator/health` are on the management port
    **8081**, not 8080 (the chart's probes use it, and the Service exposes it): to check them by hand, run
    `kubectl -n helixiam port-forward svc/helixiam 8081:8081` and
    `curl -fsS http://localhost:8081/actuator/health/readiness`.

    Check the server:

    ```bash
    curl -s http://localhost:8080/realms/master/.well-known/openid-configuration | jq .issuer
    ```

    **5. Sign in** at <http://localhost:8090> as `admin`, with the password from the Secret:

    ```bash
    kubectl -n helixiam get secret helixiam-secrets -o jsonpath='{.data.admin-password}' | base64 --decode; echo
    ```

    **6. Set up the second factor.** The chart keeps MFA on (`config.mfaEnabled=true`), so the first sign-in asks
    you to enrol an authenticator app:

    1. On **Set up your authenticator app**, scan the QR code with an authenticator app, or choose **Show code**
       to enter the setup key by hand.
    2. Enter the 6-digit code from the app and choose **Verify**.
    3. On **Save your recovery codes**, store the codes somewhere safe (each signs you in once if you lose the
       app; they are shown only now), then choose **I saved them, continue**.

    **Verify the chart's signature** (optional; needs [cosign](https://docs.sigstore.dev/cosign/system_config/installation/)).
    The release workflow signs the chart, like the images, keyless with cosign (the certificate identity names the
    signing workflow; it is not a page you can open). `cosign` uses the registry login from `docker login ghcr.io`:

    ```bash
    cosign verify ghcr.io/melmarzgioui/helixiam/charts/helixiam:1.1.0-rc.14 \
      --certificate-identity https://github.com/melmarzgioui/helixiam/.github/workflows/release.yml@refs/tags/v1.1.0-rc.14 \
      --certificate-oidc-issuer https://token.actions.githubusercontent.com
    ```

    The same command with `ghcr.io/melmarzgioui/helixiam/helix-iam-server:v1.1.0-rc.14` or
    `ghcr.io/melmarzgioui/helixiam/helix-dashboard:v1.1.0-rc.14` in place of the chart verifies the images.

    **Clean up.** Stop the port-forwards with ++ctrl+c++, then:

    ```bash
    helm -n helixiam uninstall helixiam
    kubectl delete namespace helixiam
    ```

    !!! info "For production"
        Use your own PostgreSQL, an ingress with TLS and real `https://` base URLs, the console on its own
        (internal) beheer host, `config.adminAllowedHosts` so the admin API answers only there,
        `config.trustedProxies` for the ingress, and Redis or several replicas for high availability. See
        [Install](install.md) and [High availability](../operations/high-availability.md).

=== "Local machine"

    For development: the server runs as a plain jar with the `dev` profile, against a local PostgreSQL.

    !!! info "Requires a HelixIAM licence"
        You receive registry credentials and the release files from KubeDNA B.V. No licence yet? Contact
        [contact@helixiam.com](mailto:contact@helixiam.com).

    **You need** the server jar provided with your licence (`helix-iam-server-1.1.0-rc.14.jar`), your registry
    credentials (for the console image), Java 25 (`java -version`; the jar is built for Java 25 and does not start
    on Java 21), PostgreSQL 16 (a local install, or Docker for one container) and Docker for the console.

    **1. The jar.** Put `helix-iam-server-1.1.0-rc.14.jar` in a working directory. If it came with a `.sha256`
    file, check it:

    ```bash
    shasum -a 256 -c helix-iam-server-1.1.0-rc.14.jar.sha256
    ```

    **2. PostgreSQL.** Database `helixiam` on `localhost:5432`, user `helix`, password `helix` (the same as the
    compose file). In one container:

    ```bash
    docker run -d --name hlx-pg -e POSTGRES_USER=helix -e POSTGRES_PASSWORD=helix \
      -e POSTGRES_DB=helixiam -p 5432:5432 postgres:16-alpine
    ```

    With a local install instead, create that database and user, or pass your own credentials in step 3 as
    `DB_USERNAME` and `DB_PASSWORD` (and `DB_HOST`, `DB_PORT`, `DB_NAME` when they differ).

    **3. Start the server.** The `dev` profile supplies the local URLs (`http://localhost:8080` for the server,
    `http://localhost:8090` for the console), a demo encryption key and sessions in PostgreSQL, so no Redis.
    `DB_USERNAME` and `DB_PASSWORD` are the database credentials of step 2, `HELIX_CONSOLE_BASE_URL` is the
    console's origin (the redirect URI of the built-in `helix-console` sign-in client) and `HELIX_ADMIN_PASSWORD` the
    first admin's password:

    ```bash
    DB_USERNAME=helix DB_PASSWORD=helix HELIX_CONSOLE_BASE_URL=http://localhost:8090 HELIX_ADMIN_PASSWORD=admin \
      java -jar helix-iam-server-1.1.0-rc.14.jar --spring.profiles.active=dev
    ```

    In another terminal:

    ```bash
    until curl -fsS http://localhost:8080/actuator/health/readiness; do sleep 5; done
    curl -s http://localhost:8080/realms/master/.well-known/openid-configuration | jq .issuer
    ```

    **4. Start the console** on port 8090, from the console image, pointed at the server on your machine. Log in to
    the registry first; the command depends on how Docker runs:

    ```bash
    echo "$REGISTRY_TOKEN" | docker login ghcr.io -u "$REGISTRY_USER" --password-stdin
    ```

    === "Linux (Docker Engine)"

        ```bash
        docker run --rm --network host \
          -e HELIX_CONSOLE_LISTEN=127.0.0.1:8090 -e HELIX_API_TARGET=http://127.0.0.1:8080 \
          ghcr.io/melmarzgioui/helixiam/helix-dashboard:<tag>
        ```

        The container shares the host network, so the console reaches the server on `127.0.0.1` and listens on
        `127.0.0.1:8090`. This works with a default-deny firewall (such as `ufw`): nothing to open.

        Do not use the bridge network with `--add-host host.docker.internal:host-gateway` instead. That traffic goes
        from the container to the host, and a host firewall that denies incoming connections by default drops it, so
        the console's sign-in times out.

        !!! note "Needs a console image newer than v1.1.0-rc.14"
            `HELIX_CONSOLE_LISTEN` is in the console images released after `v1.1.0-rc.14`; use the tag of such a
            release for `<tag>`. Until you have one, on Linux use the Docker Compose or Kubernetes (Helm) quick
            start above, which run the console for you.

    === "macOS / Windows (Docker Desktop)"

        ```bash
        docker run --rm -p 127.0.0.1:8090:8080 \
          -e HELIX_API_TARGET=http://host.docker.internal:8080 \
          ghcr.io/melmarzgioui/helixiam/helix-dashboard:v1.1.0-rc.14
        ```

        Docker Desktop routes `host.docker.internal` to your machine.

    !!! note "The console image is linux/amd64 only"
        On Apple Silicon, use the Docker Compose or Helm quick start on an amd64 host, or run the console from a
        checkout of the product with `cd helix-dashboard && npm ci && npm run dev -- --port 8090` (its dev proxy
        targets `http://localhost:8080`; override with `HELIX_API_TARGET`).

    **5. Sign in** at <http://localhost:8090> as `admin` / `admin`.

    **Stop and clean up.** Stop the server and the console with ++ctrl+c++, then remove the database:

    ```bash
    docker rm -f hlx-pg
    ```

    !!! warning "The dev profile is insecure by design"
        It uses a fixed demo encryption key, plain http, cookies without the `Secure` flag, no second factor,
        open self-registration, full error details, an anonymous Swagger UI and outbound calls to private
        addresses. Use it on your own machine only.

## Before going live

!!! info "Production checklist"
    Before going live: set the admin password (or read the generated one from `/tmp/helixiam-admin-password`
    and change it), set `IDP_BASE_URL` / `SP_BASE_URL` (Helm: `config.idpBaseUrl` / `config.spBaseUrl`) to
    your real **https** hostnames (WebAuthn requires https), keep `db-encryption` stable and back it up separately
    from the database (see [Backup](../operations/backup.md)), and walk the
    [Security hardening](../operations/security.md) guide.

## Next steps

1. Set up your shell for the API examples: [Authenticating to the API](api-authentication.md).
2. [Register your first application](../manage/applications.md) and wire an app to it with the
   [OIDC quickstart](../integration/oidc-quickstart.md) — or add a [SAML relying party](../manage/saml-clients.md).
3. Give a bot or AI assistant a first-class identity with [Agents](../integration/agents.md), or let a
   Kubernetes/CI workload authenticate keylessly with [Workload identity](../integration/workload-identity.md).
4. Read [Core concepts](concepts.md) to understand realms, applications, agents and flows.
