Running OSCAR on a FaRaFIN server¶
The bot is a single container. It opens outgoing connections only, to
discord.com, cloud.ovgu.de and bookstack.cs.ovgu.de. It listens on nothing, so
it needs no published port, no reverse proxy, no certificate and no VPN.
What it costs¶
Measured on the running container, not estimated:
| RAM | 94 MB |
| CPU, idle | 0 % |
| Image on disk | 522 MB |
| Database after months of use | 96 KB |
| Network | one request to cloud.ovgu.de every 9 minutes |
Any machine that already runs something else has room for this.
The five settings¶
They live in .env on the server and nowhere else. Never in the repository, never in
the image, never in the compose file.
| Variable | What it is |
|---|---|
BOT_TOKEN |
Discord Developer Portal → the application → Bot → Reset Token |
DISCORD_SERVER_ID |
Developer Mode on, right click the server, Copy Server ID |
TABLES_URL |
https://cloud.ovgu.de/apps/tables/, already correct in the example |
TABLES_USERNAME |
an OVGU Nextcloud login |
TABLES_PASSWORD |
an app password, from cloud.ovgu.de → Settings → Security |
One token, one bot
A Discord token may run in exactly one place. If a laptop and the server use the same token, both answer every command and the student sees two replies. Give the server its own Discord application, or stop the other one.
1. The repository on Forgejo¶
git clone --mirror https://isggit3.cs.ovgu.de/studium-lehre/discord-bot.git
cd discord-bot.git
git remote add forgejo https://<forgejo host>/<owner>/oscar.git
git push --mirror forgejo
A mirror push carries every branch and tag. After that, set the new remote as
origin in a normal clone and work there.
2. The pipeline¶
.forgejo/workflows/ci.yml is in the repository. It needs two things on the
instance:
- A runner.
forgejo-runner registeragainst the instance, with the labeldocker. One runner is enough. - Actions switched on for the repository, under Settings → Units → Actions.
It then runs on every push to main and develop, and on every pull request:
| Job | What it does |
|---|---|
lint |
pylint over src, fails under 9.5 |
test |
the full suite and mkdocs build --strict |
commitlint |
Conventional Commits on the pushed messages |
image |
builds the container and pushes it to the Forgejo registry |
The image job runs only on a branch, not on a pull request, and only after the other three pass. It needs no secret: Forgejo hands the job a token that may write packages of its own repository.
The image ends up at <forgejo host>/<owner>/oscar:latest, and every commit also
gets its own tag, so a rollback is docker pull ...:<older sha>.
3. The server¶
mkdir -p /srv/oscar && cd /srv/oscar
# only these two files are needed, not the whole repository
curl -O https://<forgejo host>/<owner>/oscar/raw/branch/main/compose.server.yaml
curl -O https://<forgejo host>/<owner>/oscar/raw/branch/main/.env.example
cp .env.example .env && $EDITOR .env && chmod 600 .env
Add the image to .env:
Log in once, with a Forgejo token that may read packages:
docker login <forgejo host>
docker compose -f compose.server.yaml pull
docker compose -f compose.server.yaml up -d
docker compose -f compose.server.yaml logs -f
A healthy start looks like this:
Finished loading 14 semesterplans
Logged in as OSCAR#0846, running build <sha>
Synced 29 commands to '<server>'
Module table warm: 410 rows in 0.3s
All 13 BookStack books answer
If any of those five lines is missing, the answer is in the log above it.
4. Updating¶
The database is a named volume, so it survives. Nothing else on the container is worth keeping.
To roll back, put the older commit sha in OSCAR_IMAGE and run the same two lines.
The database is the only irreplaceable thing¶
/database/oscar.db holds every student's programme, saved plan, module ratings and
written reviews. The image can be rebuilt from git, the settings from a password
manager. This cannot.
docker run --rm -v oscar_oscar-db:/db -v "$PWD":/out alpine \
sh -c 'cp /db/oscar.db /out/oscar-$(date +%F).db'
It is under 100 KB. Put it in whatever already backs up on that machine.
What still needs a human, twice a year¶
The module handbook links rot. The faculty renames a BookStack book whenever an edition rolls over, and on 2026-09-17 all eleven programme books went in one afternoon. The bot notices at startup and falls back to a search, so nothing breaks, but the deep links stay gone until somebody runs:
The study plans follow the regulation. When the faculty publishes a new Studien- und Prüfungsordnung, the page numbers move. See Standard study plans.
Both are jobs for the start of a semester, not for the pipeline.
Two things that will go wrong¶
The bot answers twice. Two containers share a token. Stop one.
Every command says "the application did not respond". The Tables API is
unreachable, or TABLES_PASSWORD is a login password rather than an app password.
The log says which.