FerrisKeyDocs

Getting started

This guide takes you from nothing to a working FerrisKey instance with an authenticated user. You will start the Docker stack, create a realm, register a client, create a user, and request tokens.

Prerequisites

  • Docker and Docker Compose installed
  • A terminal and a web browser
  • Ports 3333 (API), 5555 (UI) and 5432 (PostgreSQL) available

Deploy FerrisKey

Start the stack

Clone the repository and start all services:

git clone https://github.com/ferriskey/ferriskey.git
cd ferriskey
docker compose --profile registry up -d

This starts PostgreSQL, the FerrisKey API server, database migrations, and the admin console. The registry profile pulls the published images. The build profile builds the same services from your checkout instead.

Verify the deployment

Wait a few seconds for migrations to complete, then check that all services are healthy:

docker compose --profile registry ps

The API is available at http://localhost:3333 and the UI at http://localhost:5555.

Log in to the admin console

Open http://localhost:5555 in your browser and sign in with the default credentials:

  • Username: admin
  • Password: admin

You land in the master realm, the built-in realm that manages all the others.

Change default credentials

The default admin credentials are for local development only. In any other environment, set ADMIN_USERNAME, ADMIN_PASSWORD, and ADMIN_EMAIL before the first start. FerrisKey applies the password only while the admin user has none, so on a deployment that already ran, change it from the console instead.

Deploy without cloning the repository

The quickstart above clones the repository, but you don’t need the source to run FerrisKey. The API, web app, and database migrations all ship as published images on ghcr.io/ferriskey. To deploy on a server, save the following as docker-compose.yml and start it directly, with no checkout involved:

services:
  db:
    image: postgres:18.2
    environment:
      POSTGRES_DB: ferriskey
      POSTGRES_USER: ferriskey
      POSTGRES_PASSWORD: ferriskey
    volumes:
      - pgdata:/var/lib/postgresql
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -d ferriskey"]
      interval: 10s
      timeout: 10s
      retries: 5
      start_period: 30s

  db-migrations:
    image: ghcr.io/ferriskey/ferriskey-api
    entrypoint: ["sqlx"]
    command: ["migrate", "run", "--source", "/usr/local/src/ferriskey/migrations"]
    environment:
      DATABASE_URL: postgresql://ferriskey:ferriskey@db/ferriskey
    restart: "no"
    depends_on:
      db:
        condition: service_healthy

  api:
    image: ghcr.io/ferriskey/ferriskey-api
    environment:
      ALLOWED_ORIGINS: http://localhost:5555
      WEBAPP_URL: http://localhost:5555
      DATABASE_HOST: db
      DATABASE_NAME: ferriskey
      DATABASE_USER: ferriskey
      DATABASE_PASSWORD: ferriskey
    ports:
      - 3333:3333
    depends_on:
      db-migrations:
        condition: service_completed_successfully

  webapp:
    image: ghcr.io/ferriskey/ferriskey-webapp
    environment:
      API_URL: http://localhost:3333
    ports:
      - 5555:80

volumes:
  pgdata:
    name: ferriskey_pgdata

Then start the stack:

docker compose up -d

The migration files are baked into the ferriskey-api image (under /usr/local/src/ferriskey/migrations), so the db-migrations service applies the schema without any checked-out source. These are the same images the repository’s registry profile uses, extracted into a self-contained file you can drop onto any host or convert to a Docker Swarm stack.

Pin a version for production

The images above track the latest tag. For reproducible deployments, pin an explicit tag on the api, db-migrations and webapp services, for example ghcr.io/ferriskey/ferriskey-api:0.10.0. Tags carry no v prefix. A tag such as 0.10 follows the patch releases of that minor version. Using the same tag on api and db-migrations keeps the binary and the schema in step.

Upgrading FerrisKey

Schema changes ship inside the API image, so upgrading a running stack means pull, migrate, restart.

Pull the new images

docker compose pull

Apply new migrations

The db-migrations service runs sqlx migrate run, which only applies migrations that have not run yet, so it is safe on every upgrade:

docker compose up -d db-migrations

Restart the application

Once migrations complete, recreate the API and web app on the new images:

docker compose up -d api webapp

sqlx migrate run handles the schema. The API then runs its own data migrations every time it starts, after the schema is in place: it applies the pending ones in version order, logs each one, and records it so it never runs twice. If one fails, the API stops with Migration v<N> '<name>' failed: <reason> and does not start. Fix the cause and start it again, and it resumes from the migration that failed.

Keep api and migrations on the same tag

Always pull the api and db-migrations services from the same image tag. A newer API against an older schema, or an older API against a newer one, can fail at startup.

Create your first realm

A realm is an isolated tenant. Users, clients, roles, and credentials all live inside a realm.

Create a realm

Click the realm name in the top bar, next to the FerrisKey logo, then Create realm. Enter my-app as the Name and click Create realm.

The name appears in URLs and tokens and cannot be changed later. It takes letters, digits, hyphens and underscores.

Work in the new realm

The console opens the new realm as soon as it exists. All subsequent operations happen within it. To come back to it later, pick my-app in the same selector.

Register a client

A client represents an application that uses FerrisKey for authentication.

Create a client

In the sidebar, click Clients, then New client. Choose OpenID Connect and fill in:

  • Client ID: my-frontend
  • Name: my-frontend
  • Client authentication: Public (for a single-page application)

The protocol and the choice between public and confidential cannot be changed after creation.

Enable direct access grants

If you want to test authentication directly via the API (password grant), switch Direct access grants on in the Capability config section.

Save the client

Click Create client. Redirect URIs are not part of this form. You add them later on the client’s Settings tab, under Access, when you connect a real application. The password grant below does not use them.

Create a user

Create a user

In the sidebar, click Users, then New user. Fill in:

  • Username: alice
  • First and last name: Alice and Smith
  • Email: alice@example.com

Click Create user. The account starts without any credential.

Set a password

Open the user’s Credentials tab and choose Set a password. Under Validity, pick Permanent unless you want to force a password change at the first sign-in, then enter the New password, confirm it, and click Set the password.

Authenticate

With direct access grants enabled, you can request tokens directly from the API:

curl -X POST http://localhost:3333/realms/my-app/protocol/openid-connect/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=password" \
  -d "client_id=my-frontend" \
  -d "username=alice" \
  -d "password=your-password"

A response containing an access_token, a refresh_token, and optionally an id_token means the user authenticated.

Tokens for the management API

The routes that manage a realm (users, clients, roles and the rest of the admin API) check more than the signature and the caller’s permissions. The token must carry <realm>-realm in its aud claim, where <realm> is the realm that issued it, and its azp claim must name a client that is enabled and was created by an administrator.

A token that fails either check gets 401. That covers a token requested for a resource server (the resource parameter replaces the audience) and a token issued to a client that registered itself through dynamic registration or a client metadata document.

Connect a real application

The password grant above is fine for a quick API test, and nothing else: it is deprecated. Real applications use browser-based SSO with the authorization code flow, and a CLI uses the device code grant.

To connect a dashboard, an internal service, or a self-hosted tool, follow the application SSO guide next. It walks through which console screens to use, what values to copy, and how to debug the usual setup mistakes.

What’s next