FerrisKey: an IAM service written in Rust for cloud-native teams
FerrisKey is an open-source IAM solution designed for modern cloud-native environments, high-performance and written in Rust.
At a glance
- What is it?
- FerrisKey is an open-source identity and access management platform built in Rust with a hexagonal layout, OIDC and OAuth2 flows, TOTP MFA and multi-tenant realms. It is pre-1.0, but the deployment paths are documented and the code is shipped as a real Cargo workspace rather than a single binary.
- Who is it for?
- FerrisKey fits teams that need standards-based OIDC and OAuth2 with realm-level tenant isolation, want a single language across the identity service, and can tolerate a pre-1.0 project with a fast-moving Cargo layout. It is the wrong choice for organisations that need a mature commercial support contract, an audited multi-year upgrade path, or protocol coverage beyond what the project advertises.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A lighter IAM alternative for teams running on Kubernetes
FerrisKey is an identity and access management platform written in Rust and organised around a hexagonal architecture, which the project describes as a clean domain with clear ports and adapters. The problem it addresses is straightforward: identity providers that grew out of enterprise Java stacks tend to arrive with heavy runtime footprints, sprawling configuration and deployment assumptions that no longer fit a Kubernetes cluster. FerrisKey targets the same functional ground, offering OIDC and OAuth2 flows, multi-tenant realms, clients and service accounts, TOTP based multi-factor authentication and an observability story built on Prometheus metrics and Grafana dashboards.
The audience is cloud-native teams that already think in containers, Helm charts and OCI distribution. The repository topics list iam, oauth2, oidc, cloud-native and react alongside Rust and TypeScript, so there is a web console as well as a backend. The project is licensed Apache-2.0, which matters for a component that sits in front of internal applications, and the licence line in the project description makes the no-paywall, community-first stance explicit.
What makes this worth a closer look is that the workspace is real. Version 0.9.0 was released on 2026-10-03, the workspace manifest declares edition 2024, and the container build compiles named binaries rather than a script that happens to start an API. It is still pre-1.0, so the version number is the clearest signal of maturity in the project itself.
One Cargo workspace, roughly fifty crates
The repository is a single Cargo workspace whose members list in Cargo.toml reads core, api, operator, client, a benches crate, and then a long set of library crates under libs. Those library crates are named by domain, and the names hint at the internal vocabulary: ferriskey-domain holds the domain model, ferriskey-security and ferriskey-password-policy sit next to it, and ferriskey-authz, ferriskey-authz-cedar, ferriskey-saml and ferriskey-consent cover protocol and policy concerns.
The remainder of the libs directory is populated by vertical slices that are named like ferryboats: maskass, trident, abyss, aegis, compass, seawatch, plus mail, webhook, migrate, organization, portal-theme and portal-layouts. A parallel set of ferriskey-api-* crates mirrors those domains for the HTTP layer, including ferriskey-api-realm, ferriskey-api-client, ferriskey-api-user, ferriskey-api-account, ferriskey-api-authentication and ferriskey-api-consent. The api directory is the axum service, front is the React and TypeScript console, and operator plus charts cover Kubernetes.
This shape matters for upgrades. A change to realm or authentication behaviour touches a domain crate and its matching api crate rather than a monolithic service, but it also means the dependency graph is wide and every crate in the workspace is a candidate for a version bump. The manifest itself notes that centralised dependency versions are inherited by member crates through workspace = true, and that only the freshly extracted api feature crates use that mechanism today, with the older crates slated for a follow-up refactor. That is a candid detail: the workspace cleanup is not finished.
Tenant realms are the isolation boundary, and ALLOWED_ORIGINS sits above them
Multi-tenancy in FerrisKey is organised around realms, and the project describes these as logical isolation of users, roles, clients and secrets. A realm is therefore the unit you would map onto a customer, a business unit or an environment. On top of that, clients and service accounts carry fine-grained role mapping, and the feature table describes the role system as bitwise, which lets permissions be composed rather than enumerated one by one.
CORS handling has two layers, and the difference matters. The documented environment variable ALLOWED_ORIGINS lists the origins accepted on every route. Beyond it, each client declares its own web origins at runtime, and those are enforced per realm. The configuration example in the project documentation sets ALLOWED_ORIGINS=http://localhost:5555 for the local web console, while the compose file widens it to http://localhost:5555,http://127.0.0.1:5555 on the build profile and narrows it back to a single origin on the registry profile.
Because the global list applies on every route, a careless value there loosens cross-origin access for endpoints that have nothing to do with the browser console. Per-realm client origins are the tighter control, and they are runtime state rather than deployment configuration, so they can change without a restart but also cannot be audited by reading an environment file. Plan the origin list for the console and let client records carry the rest.
Two compose profiles that land on two different console ports
The quickest evaluation path is Docker Compose, and the project documentation offers four options, of which two are compose profiles. Option A pulls the published images, and Option B rebuilds from source. The commands differ only in the profile flag:
docker compose --profile registry up -dThat is Option A, and the documentation says to visit http://localhost:5556 afterwards for the console, with the default credentials admin and admin. Option B uses a different profile:
docker compose --profile build up -dand the console for that path is documented at http://localhost:5555 with the same admin and admin defaults. Two profiles, two ports, one pair of default credentials. If you follow the quick start without noting which profile you started, the port you try will simply refuse connections, and the credentials are public defaults that should be changed before the instance is exposed.
The compose file itself is worth reading before you start. The database service defaults to ${POSTGRES_IMAGE:-postgres:18.2}, maps port 5432, and mounts a pgdata volume at /var/lib/postgresql, the Postgres 18 recommended mount point, with a pg_isready healthcheck against the ferriskey database. The api-build service depends on db-migrations-build completing successfully, and that service runs sqlx migrate run as its entrypoint against the migrations copied into the image. The webapp-build service maps host port 5555 to container port 80 and points the frontend at API_URL=http://localhost:3333, matching the 3333:3333 mapping on the API service.
There is a migration trap here. The documentation warns that a local compose setup now mounts pgdata at the Postgres 18 recommended path, so a volume created under the older mount point must be removed or migrated first, and it names the destructive shortcut docker compose down -v. If you have an existing local FerrisKey database from an earlier layout, that reset is the fastest route but it deletes the data with it.
Running the API and the frontend from Cargo with sqlx migrations
For developers who want to work on the service itself, Option D bypasses containers for the application and keeps Postgres in Docker. The documented sequence starts by fetching the source:
git clone https://github.com/ferriskey/ferriskeyNext you launch the database and apply migrations with sourced environment variables:
cd api
cp env.example .env
# feel free to change the env variables in .env to your liking.
docker compose up -d
cd ../core
# to install sqlx you might need to run `cargo install sqlx-cli --no-default-features --features postgres`
DATABASE_URL=postgres://ferriskey:ferriskey@localhost:5432/ferriskey sqlx migrate runThen the API starts on its own:
# to upgrade rustc version to 1.89.0, you can run `rustup update`
cd ../api
cargo runThe frontend is optional and runs as a separate process:
cd ../front
cp .env.example .env
pnpm install
pnpm run devTwo details deserve attention. The rustc note points at 1.89.0 for local development even though the container build uses rust:1.99.0-trixie, so a local toolchain older than the release image can behave differently. And migrations are applied by the sqlx CLI rather than by the API on boot, which means an upgrade needs an explicit migration step in your runbook instead of happening automatically when the new version starts. The project documentation also lists a Database Migrations section of its own for the operator path.
Distroless images that ship only the API and the operator
The Dockerfile is built for small, boring images. A chef stage installs cargo-chef and sqlx-cli, a planner stage produces a recipe, and a builder stage cooks dependencies before compiling the real source. The build line is explicit about what ships:
RUN cargo build --release --bin ferriskey-api --bin ferriskey-operatorOnly those two binaries are copied into the runtime layer, along with the migrations directory and the sqlx binary. The runtime base is gcr.io/distroless/cc-debian13:nonroot, chosen because it ships glibc, libgcc_s, libssl3 and libcrypto3 in roughly 34 MB against roughly 106 MB for debian:bookworm-slim with equivalent packages.
The distroless base has no shell and no package manager, and the Dockerfile comment is careful about why that is safe: every entrypoint used against the images, including ferriskey-api, ferriskey-operator and sqlx migrate run, is an exec of a binary on PATH, so nothing depends on /bin/sh. That is a deliberate constraint with a practical consequence. You cannot debug a running container by opening a shell, and any custom entrypoint has to be a binary that exists in the image. The api stage exposes port 80.
The compose file pulls the published image from ghcr.io/ferriskey/ferriskey-api, and the Kubernetes path is a Helm chart under charts/ferriskey with its own documentation and OCI distribution. The chart needs a reachable Postgres, either one you run or one your platform provides through its own operator.
Where FerrisKey is the wrong tool
Several limits are visible in the documentation rather than hidden. First, the version line: 0.9.0 means the public interface can still move, and the workspace manifest itself admits that the dependency-version inheritance refactor applies only to the newest api crates, with the rest queued for later. If you need an identity provider whose API contract stays fixed for years, this is a poor fit today.
Second, protocol coverage is narrower than the crate layout suggests. The feature table advertises OIDC and OAuth2 plus TOTP MFA, while the workspace contains ferriskey-saml and ferriskey-api-saml crates. The presence of SAML code in the tree is not the same as a documented, supported SAML feature, and the documentation does not describe a SAML setup flow. Treat it as unadvertised until the project documents it.
Third, default credentials ship with the local setup. admin and admin is documented for both compose profiles and the Cargo path, so a working install is also an open install if the port is reachable. The same applies to the console ports themselves: 5555 and 5556 are development defaults, and the compose file publishes the API on 3333 and Postgres on 5432 to the host without an authentication layer in front of them.
Fourth, there is no obvious path for teams that need a vendor support contract or a signed compliance attestation. The project is community-first and self-hosted, and the documentation contains no statement about commercial support, an SLA or a formal security audit. The repository does ship SECURITY.md, a CODE_OF_CONDUCT.md and a GOVERNANCE.md, which describe how the community handles these matters, but that is governance structure rather than a guarantee.
Editorial conclusion
FerrisKey fits teams that need standards-based OIDC and OAuth2 with realm-level tenant isolation, want a single language across the identity service, and can tolerate a pre-1.0 project with a fast-moving Cargo layout. It is the wrong choice for organisations that need a mature commercial support contract, an audited multi-year upgrade path, or protocol coverage beyond what the project advertises. Before adopting it, run the build profile locally, confirm the console at http://localhost:5555 answers, verify that sqlx migrate run applies cleanly against your Postgres version, and decide what replaces the default admin and admin credentials before the instance is reachable by anything else.
Frequently asked questions
How do I start FerrisKey locally and where does the console live?
Run docker compose --profile registry up -d for the published images, which the documentation places at http://localhost:5556, or docker compose --profile build up -d to rebuild from source, which lands on http://localhost:5555. Both paths sign in with the default credentials admin and admin.
Does FerrisKey support multi-tenancy and OIDC logins?
Yes. Realms provide logical isolation of users, roles, clients and secrets, and the platform implements OIDC and OAuth2 flows alongside TOTP multi-factor authentication with pluggable strategies and required actions.
What database does FerrisKey use and how are migrations applied?
It uses Postgres, reached through the DATABASE_URL setting, and migrations are applied by the sqlx CLI rather than automatically at startup. In the compose setup a dedicated db-migrations-build service runs sqlx migrate run against /usr/local/src/ferriskey/migrations before the API service starts.
Can FerrisKey be deployed on Kubernetes?
There is an official Helm chart under charts/ferriskey with its own documentation and OCI distribution, and the API image is published at ghcr.io/ferriskey/ferriskey-api. The chart expects a reachable Postgres, run either separately or through your platform's operator.
How current is FerrisKey and what licence does it use?
The last push was on 2026-10-19, and version 0.9.0 was released on 2026-10-03. FerrisKey is distributed under the Apache-2.0 licence with no paywall, as stated in the project description and the licence file in the repository root.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/ferriskey-ferriskey)Was this useful?