Geyser is a web application that streamlines the course assignment process in educational institutions. It manages the complete workflow from initial teacher requests through commission decisions to final assignments. Built with PostgreSQL, Hasura GraphQL, NestJS, and Keycloak authentication, it provides a secure and efficient "Course Assignment Flow".
Note: This documentation is intended for system administrators and developers who need to install, configure, and maintain Geyser. End-user documentation will be provided separately.
This guide will get you a working instance of Geyser in minutes.
- Linux or macOS
- Port 443 must be accessible (HTTPS traffic)
- A domain name with valid TLS certificates (follow these instructions to get one)
-
Docker version 25.0+ (required):
curl -fsSL https://get.docker.com | sh sudo usermod -aG docker $USER # Log out and back in after this
-
Hasura CLI (optional):
curl -L https://github.com/hasura/graphql-engine/raw/stable/cli/get.sh | bash
-
Oh My Zsh (zsh users only – required for autocomplete):
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
Download and install the latest version:
curl -fsSL https://github.com/arkandias/geyser/raw/HEAD/scripts/install.sh | sh
Or a specific version (see the release list):
curl -fsSL https://github.com/arkandias/geyser/raw/HEAD/scripts/install.sh | VERSION=1.2.3 sh
By default, Geyser will be installed in ~/.geyser/<version>
.
You can change this with:
curl -fsSL https://github.com/arkandias/geyser/raw/HEAD/scripts/install.sh | INSTALL_PATH=/opt/geyser sh
You can combine both environment variables:
curl -fsSL https://github.com/arkandias/geyser/raw/HEAD/scripts/install.sh | VERSION=1.2.3 INSTALL_PATH=/opt/geyser sh
Alternatively, you can clone the whole repository for development or for easier updates and deployment in production:
git clone https://github.com/arkandias/geyser.git
cd geyser
In this case, the installation directory is wherever you cloned the repository (e.g., ~/geyser
if you ran the git
clone command from your home directory).
Then create a symlink to geyser script in ~/.local/bin
:
mkdir -p ~/.local/bin
ln -sf "$(pwd)/cli/geyser" ~/.local/bin/geyser
And create a local configuration file that won't be tracked by git:
cp .env .env.local
Confirm the installation was successful:
geyser --version
If you get a "command not found" error, you may need to add ~/.local/bin
to your PATH:
export PATH="$HOME/.local/bin:$PATH"
Add this line to your shell configuration file (~/.bashrc
, ~/.zshrc
, etc.) to make the change permanent.
Alternatively, you can use the full path to the script:
<install-dir>/cli/geyser --version
-
Edit the file
.env
and replacegeyser.example.com
with your server's hostname. -
Add the TLS certificates for your server's hostname to the
certs/<server-hostname>/
directory as follows:certs/<server-hostname>/fullchain.crt
for the full certificate chaincerts/<server-hostname>/private.key
for the private key
-
Initialize Geyser with the following command:
geyser init
-
During initialization, you will be prompted for Keycloak temporary admin account username and password (choose a strong one!).
-
Once the initialization is finished, the following services are accessible in a web browser:
- Keycloak:
https://<server-hostname>/auth
- Geyser:
https://<server-hostname>
- Keycloak:
The Keycloak account you created during initialization is a temporary account for the realm master
.
This realm is intended to administrate the other realms only.
Thus, Geyser uses a different realm to authenticate users: the realm geyser
.
We will now create a first user in this realm, with privileges to administrate the app.
In a web browser, go to: https://<server-hostname>/auth
-
Connect to Keycloak admin console using the temporary admin account.
-
Once logged in, click on the "Manage realms" button in the left menu and select "geyser" (click on the name).
-
Now, "Geyser" must be displayed next to the "Current realm" badge. Click on the "Users" button in the left menu, and then on the "Create new user" button.
-
Fill in the "Create user" form. You must provide an email and "Email verified" must be "On" (at the top of the form). The other fields are optional. Then, click on the "Join Groups" button (at the bottom of the form).
-
In the "Select the groups to join" dialog box, check the box of the "AppAdmin" group and click on the "Join" button. This will give the user admin privileges in Geyser.
-
Now that the user is created, you will be redirected to the user "Details" tab. Click on the "Credentials" tab, and then on the "Set password" button.
-
Enter your new user's password (twice), set "Temporary" to "Off", and click on the "Save" button.
-
Go to:
https://<server-hostname>
and log into Geyser with your newly created user email and password. -
You can now refer to the app documentation to configure Geyser.
For an advanced configuration of Keycloak, you can refer to Keycloak documentation.
We simply point out some useful login options.
-
If you want to allow users to create an account in Keycloak and to recover their password, you can enable this option on the "Login" tab of the "Realm settings" page.
For these options to work, Keycloak must be configured to send emails. You can do this in the "Email" tab. Note that the current admin user must have an email address to configure Keycloak's email. You can add one on the "Users" page.
-
You may use an external identity provider, for example if your institution or your company provides one. This can be configured on the "Identity providers" page.
Note: Users with a Keycloak account will not automatically have access to Geyser. Keycloak is only used to authenticate users with their email address. Access is configured within the app.
Geyser consists of multiple services, each running in a Docker container organized into three logical tiers:
The frontend consists of an Nginx Server (frontend
) that serves as the main entry point and reverse proxy for all
external access:
https://<server-hostname>
serves the web client applicationhttps://<server-hostname>/api
proxies requests to the backend NestJS serverhttps://<server-hostname>/auth
proxies requests to the Keycloak authentication service
The frontend also handles TLS termination and serves static assets for the web application.
The backend tier consists of three interconnected services:
- NestJS Server (
backend
): The main API server accessible via the frontend proxy at/api
- Hasura GraphQL Engine (
hasura
): Provides GraphQL interface to the database - PostgreSQL Database (
db
): Stores all application data
The NestJS server can query the database either directly via SQL or through Hasura's GraphQL API.
Both the database and Hasura are isolated on a private network (private-db
) and are only accessible by the NestJS
server – they cannot be reached through the frontend proxy.
User authentication is handled by a dedicated Keycloak instance with its own PostgreSQL database:
- Keycloak (
keycloak
): OpenID Connect provider for user authentication, accessible via the frontend proxy at/auth
- Keycloak Database (
kc-db
): Dedicated PostgreSQL instance storing user credentials and identity data
The authentication flow works as follows:
- Users authenticate with Keycloak using their email address
- The backend validates tokens with Keycloak and fetches additional user information (roles, permissions)
- The backend issues its own access tokens for API requests
- Clients include these access tokens in API requests for authorization
Services are organized into three Docker networks:
public
: Connects frontend, backend, and keycloak for external-facing communicationprivate-db
: Isolates the main database and Hasura from external accessprivate-kc-db
: Isolates the Keycloak database for security
Geyser uses two environment files:
.env
: Base configuration file with default values.env.local
: Local overrides (not version-controlled)
Variables in .env.local
take precedence over those in .env
.
Environment variable | Default value | Description |
---|---|---|
GEYSER_ENV |
production |
Deployment environment [development /production ], see Development Environment |
GEYSER_DOMAIN |
localhost |
Hostname (and optionally port number) of the server (e.g., geyser.example.com ) |
GEYSER_TENANCY |
single |
Tenancy mode [single /multi ], see Multi-tenant Mode |
GEYSER_LOG_LEVEL |
info |
Logging threshold [silent /debug /info /warn /error ] |
GEYSER_AS_SERVICE |
false |
Indicate if Geyser is running as a systemd service [true /false ], see Running Geyser as a systemd service |
NGINX_AUTH_ADMIN_ALLOW |
0.0.0.0/0 |
Allowed IPs for Keycloak restricted access |
KC_DB_PASSWORD |
Required | Password for Keycloak database privileged role |
DB_PASSWORD |
Required | Password for the main database privileged role |
HASURA_GRAPHQL_ADMIN_SECRET |
Required | Hasura admin secret (bypass token authentication) |
API_ADMIN_SECRET |
Required | API admin secret (bypass token authentication) |
OIDC_CLIENT_SECRET |
Required | Secret for Keycloak app client used by backend to authenticate users |
These environment variables are used by the administration script (see Administration Script)
to compute many other variables.
Finally, all these environment variables are passed to the various services using the Compose file (compose.yaml
).
Geyser supports a development mode where core services run in Docker containers while frontend and backend run locally for faster iteration.
For complete development setup instructions, see CONTRIBUTING.md.
Geyser can run in multi-tenant mode by setting GEYSER_TENANCY=multi
.
In this configuration, multiple organizations can be hosted on the same Geyser instance without any interaction between
them.
Each organization is uniquely identified by its organization key, and the Nginx configuration is modified as follows:
https://<organization-key>.<server-hostname>
serves the web client for the organization with the corresponding keyhttps://master.<server-hostname>
serves the web client with an interface to manage all organizations (need admin privileges)https://api.<server-hostname>
proxies requests to the backend NestJS server (shared across all organizations)https://auth.<server-hostname>
proxies requests to the Keycloak authentication service (shared across all organizations)
Note: The client determines the organization key from the hostname, unless VITE_ORGANIZATION_KEY
is provided.
Note: You need a wildcard certificate for the *.<server-hostname>
subdomains
(follow these instructions to get one).
For production deployments, Geyser can be configured to run as a systemd service for automatic startup, system logging integration, Docker dependency management, and automatic restart on failure.
-
Copy the service file:
sudo cp deploy/systemd/geyser.service /etc/systemd/system/
-
Customize the service file:
sudo systemctl edit --full geyser.service
Replace
User=
,WorkingDirectory=
, and script paths inExecStart=
,ExecStop=
-
Reload systemd configuration:
sudo systemctl daemon-reload
-
Enable and start the service:
sudo systemctl enable geyser.service sudo systemctl start geyser.service
Note: With GEYSER_AS_SERVICE=true
, logs are integrated with systemd:
- The administration script uses systemd-compatible formatting for output
- Docker container logs are sent to journald instead of Docker's default JSON files
# Check service status
sudo systemctl status geyser.service
# View logs in real-time
journalctl -u geyser.service -f
# View recent logs
journalctl -u geyser.service --since "1 hour ago"
# Start/stop/restart the service
sudo systemctl start geyser.service
sudo systemctl stop geyser.service
sudo systemctl restart geyser.service
# Disable automatic startup
sudo systemctl disable geyser.service
The geyser
script provides a comprehensive set of commands to manage your installation:
init
: Initialize a fresh Geyser installationstart
: Start Geyser servicesstop
: Stop Geyser servicesrestart
: Restart Geyser servicesshow
: Show Geyser servicesupdate
: Update Geyser servicescleanup
: Cleanup Geyser servicespurge
: Completely remove Geyser installation and all data
data-backup
: Backup Geyser main databasedata-restore
: Restore Geyser main databasekeycloak-export
: Export Keycloak realms and userskeycloak-import
: Import Keycloak realms and userswebdav-upload
: Upload a file or directory to a WebDAV server
compose
: Docker Compose wrapperhasura
: Hasura CLI wrapperkc
: Keycloak CLI accesskcadm
: Keycloak Admin CLI accesswebhook
: Webhook wrapperwebhook-start
: Start a webhook on port 9000webhook-stop
: Terminate any process listening on port 9000
install-completion
: Install zsh completion (using oh-my-zsh)
-h, --help
: Show help message-v, --version
: Show version information--log-level LEVEL
: Set log level [silent
/debug
/info
/warn
/error
]--silent
: Set log level to silent--debug
: Set log level to debug--env ENV
: Set deployment environment [development
/production
]--dev
: Set deployment environment to development--prod
: Set deployment environment to production--domain DOMAIN
: Set domain name for deployment (e.g.,geyser.example.com
)--as-service
: Run as a systemd service (affects logging)
Run geyser COMMAND --help
for more information on a command.
Shell completion for zsh can be installed with:
geyser install-completion
Unless Geyser is running as a systemd service, the logs produced by the script
are stored in logs/geyser.log
.
The script scripts/backup.sh
is provided to automatically create a backup of Geyser and upload it to a WebDAV server
(e.g., Nextcloud).
It can be scheduled via crontab for regular backups.
Configuration:
- Required environment variables:
WEBDAV_URL
: Base URL of the WebDAV serverWEBDAV_USER
: WebDAV usernameWEBDAV_PASS
: WebDAV passwordWEBDAV_DIR
: Directory for uploads on the WebDAV server
- Can be set in
.env
,.env.local
, or environment
Example crontab entries:
# Hourly backups in June and July
0 * * 6,7 * <install-dir>/scripts/backup.sh
# Daily at 3:00 AM other months
0 3 * 1-5,8-12 * <install-dir>/scripts/backup.sh
Geyser supports updates and continuous deployment, but the approach depends on your installation method:
If you installed Geyser by cloning the repository, you can use:
scripts/deploy.sh
This command pulls the latest code, updates Docker images, apply database migrations, and restarts services.
You can automate deployment on repository updates with a webhook as follows:
-
Setup webhook environment variables in
.env
or.env.local
:WEBHOOK_SECRET=<webhook-secret>
-
Start webhook server:
geyser webhook-start
-
To stop the webhook server:
geyser webhook-stop
The default webhook configuration (deploy/webhook/hooks.json
) defines a deploy
hook that triggers when:
- A GitHub Actions workflow named "CI" completes successfully
- The workflow was triggered by a push on branch
master
- The webhook signature is verified using
WEBHOOK_SECRET
Note: The default configuration is designed for GitHub repositories, but it can be adapted for other Git hosting
services by modifying the trigger rules in deploy/webhook/hooks.json
.
To use the default webhook, add a webhook in your GitHub repository settings with the following parameters:
- Payload URL:
https://<server-hostname>:9000/hooks/deploy
- Content type:
application/json
- Secret:
<webhook-secret>
- "Enable SSL verification"
- "Send me everything" or select "Workflow runs"
Note: The webhook runs on port 9000 with TLS. Ensure your firewall allows this port and use a strong webhook secret.
Note: If Geyser is running as a systemd service, you can run the webhook
as a service too, using the service file deploy/systemd/geyser-webhook.service
(see systemd setup instructions).
Otherwise, the webhook logs will be stored in logs/webhook.log
.
If you used the install script, updates require manual reinstallation:
-
Backup your data (recommended):
geyser data-backup geyser keycloak-export
-
Download and install the new version as described in the installation step
-
Copy configuration files and data:
export OLD_INSTALL_PATH=<old-install-dir> export NEW_INSTALL_PATH=<new-install-dir> cp "${OLD_INSTALL_PATH}"/.env "${NEW_INSTALL_PATH}"/ cp "${OLD_INSTALL_PATH}"/.env.local "${NEW_INSTALL_PATH}"/ cp -r "${OLD_INSTALL_PATH}"/backups "${NEW_INSTALL_PATH}"/ cp -r "${OLD_INSTALL_PATH}"/keycloak/backups "${NEW_INSTALL_PATH}"/keycloak/ cp -r "${OLD_INSTALL_PATH}"/certs "${NEW_INSTALL_PATH}"/ cp -r "${OLD_INSTALL_PATH}"/keys "${NEW_INSTALL_PATH}"/ cp -r "${OLD_INSTALL_PATH}"/logs "${NEW_INSTALL_PATH}"/
-
Now
geyser
should point to the new version script. Update Docker images and restart services:geyser update
Bug reports, feature requests, and pull requests are welcome on GitHub, see CONTRIBUTING.md for detailed instructions.
This project is licensed under the GNU Affero GPL v3 – see the LICENSE file for details.