Skip to main content

Run identity-service

This page takes you from an empty folder to a working sign-in. You start identity-service locally with Docker Compose, create a company, connect a Shellui app, then go through the settings a production deployment needs.

Start it locally​

Docker Compose starts identity-service with DEBUG=true, SQLite, and a Redis container. You need Docker and Git:

git clone https://github.com/shellui/identity-service.git
cd identity-service
cp .env.example .env

.env.example holds only the required settings, with DEBUG=true for local runs. Optional settings are commented out with their default, see Configuration. Open .env and set SECRET_KEY to a long random value. Django uses it to sign sessions and CSRF tokens, and in local development it also signs the JWTs. To generate one:

python3 -c "import secrets; print(secrets.token_urlsafe(50))"

Then build and start the stack:

docker compose up --build

The container runs the database migrations, then serves on http://localhost:8000. Three pages are useful right away:

  • /: on an empty database, a one-time form creates the first superuser (only while DEBUG=true)
  • /admin/: Django admin, where operators manage companies, users, and provider apps
  • /api/docs/: the interactive OpenAPI reference for every endpoint, also available as ReDoc at /api/docs/redoc/. Both pages are served from the service's own static files, with no CDN

To create the superuser from the command line instead:

docker compose exec -u appuser identity-service python manage.py createsuperuser

Run without Docker​

To work on the code, run identity-service with uv. With DEBUG=true and no REDIS_URL, it uses an in-process cache and skips the scheduled jobs:

uv sync
cp .env.example .env
uv run python manage.py migrate
uv run python manage.py runserver

Create a company​

Every sign-in belongs to a company (a tenant). In Django admin, open Companies and add one. Note its ID: the shell sends it as company_id on every sign-in.

New companies have magic link sign-in turned on. In local development, emails go to the console backend, so the sign-in link shows up in the container logs:

docker compose logs -f identity-service

Staff accounts, including the superuser you created, cannot sign in with a magic link: they get a notice instead of a link (see Staff accounts). Test magic links with a regular address, or sign in with OAuth.

To add OAuth, create an app at the provider with the callback URL http://localhost:8000/api/v1/oauth/callback, then add its client ID and secret in Shellui admin under OAuth setup (or with POST /api/v1/oauth-social-apps). OAuth providers lists every supported provider and its settings.

Connect your Shellui app​

Point the shell at identity-service in shellui.config.json. companyId is the company you created, and login lists the methods the login page may offer:

{
"backend": {
"type": "shellui",
"url": "http://localhost:8000",
"companyId": 1,
"login": {
"methods": ["magic_link", "oauth"],
"oauthProviders": ["github"]
}
}
}

The shell intersects that list with what the company has enabled, read from GET /api/v1/settings. While DEBUG=true, identity-service accepts any loopback redirect_to, so a shell on http://localhost:4000 works without more setup. Every other shell origin must be on the company redirect allowlist, see OAuth login. The shell side of this setup is described in Connect a backend.

Deploy to production​

The Docker image defaults to DEBUG=false and refuses to start until the production settings are in place. Use the shellui/identity-service image with a pinned tag, for example shellui/identity-service:0.9.2.

Redis is required

With DEBUG=false, the container exits at startup when REDIS_URL is unset. Redis backs rate limits, the logout denylist, OAuth and SAML state, and the scheduled jobs.

Start by generating the settings with the image itself:

docker run --rm shellui/identity-service:0.9.2 \
python manage.py generate_env --host auth.example.com > identity.env

It needs no env, database or Redis. It prints the required settings with a fresh SECRET_KEY and JWT_PRIVATE_KEY, and the URLs derived from --host. Replace every change-me, then paste the block into Coolify or your secret manager and delete the file. --role global or --role region adds that role's required settings as placeholders. --optional also lists every optional setting with its default.

The block holds these variables:

VariableValue
SECRET_KEYA long random value, kept secret
JWT_PRIVATE_KEYRS256 private key, see below
JWT_ISSUERPublic HTTPS URL of identity-service, for example https://auth.example.com
JWT_AUDIENCEshellui, or the audience your APIs expect
ALLOWED_HOSTSHostname of identity-service, for example auth.example.com
CSRF_TRUSTED_ORIGINShttps://auth.example.com, for Django admin and the sign-in pages
REDIS_URLFor example redis://redis:6379/0
POSTGRES_DATABASE_URLRecommended. Without it, SQLite is stored in the /app/data volume

To generate only a new signing key, for example to rotate it, the image prints JWT_PRIVATE_KEY and JWT_KEY_ID:

docker run --rm -e SECRET_KEY=temporary \
shellui/identity-service:0.9.2 python manage.py generate_jwt_keys

In a secret manager or the Coolify UI, paste only the PEM value, without the surrounding quotes. JWT and JWKS explains key formats and rotation.

Then finish the deployment:

  1. The image has a built-in health check on GET /health/live (port 8000), which Coolify detects automatically. Leave Coolify's own health check off, or point it at the same path. It does not touch the database.
  2. Behind a reverse proxy, list the proxy addresses in TRUSTED_PROXY_IPS so rate limits see the real client IP.
  3. Create the first superuser with python manage.py createsuperuser in the container.
  4. Add each production shell origin to the company redirect allowlist.
  5. Run ./tools/prod-config-check.sh https://auth.example.com from a checkout to check the live configuration.

Every variable is described in Configuration. The production defaults for HTTPS, cookies, and rate limits are in Security hardening.

Next steps​

Once sign-in works, continue with the guide that matches your product:

  • Company access: restrict who can join, and send invitations
  • Webhooks: call your automation when users join or leave
  • Scheduled jobs: check the built-in jobs, or move them to their own container