OpenGate/ Docs

Import / Export

OpenGate IAM supports importing and exporting realm configuration, users, roles, and clients via the Admin API. This enables migrating data between environments, seeding a new realm, or creating configuration backups.

What Can Be Exported

ResourceEndpointNotes
Realm configurationGET /admin/realms/{realmId}/exportSettings, token lifespans, SMTP config
UsersGET /admin/realms/{realmId}/users/exportHashed credentials are not included
RolesGET /admin/realms/{realmId}/roles/exportAll roles and role mappings
OAuth ClientsGET /admin/realms/{realmId}/clients/exportClient secrets are redacted

Passwords are not exported

User passwords (BCrypt hashes) are excluded from exports for security. Exported users will need to reset their passwords or you must import hashed credentials via a database-level migration.

Exporting a Realm

# Authenticate with the Admin API
TOKEN=$(curl -s -X POST http://localhost:8080/realms/master/protocol/openid-connect/token \
-d "grant_type=client_credentials" \
-d "client_id=admin-cli" \
-d "client_secret=your-secret" | jq -r .access_token)

# Export full realm configuration
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8089/admin/realms/my-realm/export \
-o my-realm-export.json

# Export users only
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:8089/admin/realms/my-realm/users/export" \
-o my-realm-users.json

Export Format

my-realm-export.jsonjson
{
"realm": {
  "id": "my-realm",
  "displayName": "My Application",
  "accessTokenLifespan": 300,
  "refreshTokenLifespan": 2592000,
  "mfaRequired": false,
  "smtpConfig": { "host": "smtp.example.com", "port": 587 }
},
"roles": [
  { "name": "admin", "description": "Full access" },
  { "name": "viewer", "description": "Read-only access" }
],
"clients": [
  {
    "clientId": "my-app",
    "redirectUris": ["https://app.example.com/callback"],
    "grantTypes": ["authorization_code", "refresh_token"]
  }
],
"users": [
  {
    "username": "alice",
    "email": "alice@example.com",
    "enabled": true,
    "roles": ["viewer"]
  }
]
}

Importing a Realm

# Create a new realm from an export file
curl -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d @my-realm-export.json \
http://localhost:8089/admin/realms/import

# Import users into an existing realm
curl -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d @my-realm-users.json \
http://localhost:8089/admin/realms/my-realm/users/import

Import Behaviour

ScenarioDefault Behaviour
Realm already existsReturns 409 Conflict — use ?overwrite=true to replace
User already existsSkipped — use ?mergeUsers=true to update attributes
Client already existsReturns 409 Conflict
Unknown role in user mappingRole is created automatically
# Overwrite existing realm (destructive)
curl -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d @my-realm-export.json \
"http://localhost:8089/admin/realms/import?overwrite=true"

Overwrite is destructive

?overwrite=true deletes and recreates the realm, including all users and sessions. Use with caution in production.

Database-Level Migration

For migrating large user bases with password hashes, use PostgreSQL pg_dump:

# Dump users from source environment
pg_dump -h source-host -U opengate -t users opengate_users > users.sql

# Restore to target environment
psql -h target-host -U opengate opengate_users < users.sql

Seeding a Realm (CI/CD)

Place a realm seed file in your repository and apply it during deployment:

scripts/seed-realm.shbash
#!/bin/bash
set -e

TOKEN=$(curl -sf -X POST $OPENGATE_URL/realms/master/protocol/openid-connect/token \
-d "grant_type=client_credentials&client_id=admin-cli&client_secret=$ADMIN_SECRET" \
| jq -r .access_token)

curl -sf -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d @config/realm-seed.json \
"$OPENGATE_URL/admin/realms/import?overwrite=false"

echo "Realm seeded successfully"

Idempotency

Run the seed script with overwrite=false (the default) to make it idempotent — it will skip the import if the realm already exists.