Opi!
A Finnish training site: it introduces new content, tests with typed exercises scheduled by spaced repetition, and corrects by showing the expected form and the rule. It runs on a Kubernetes cluster built in my homelab, updated automatically on every merge.
- Year
- 2026
- Tech stack
- Python
- Flask
- Kubernetes
- k3s
- Terraform
- Proxmox

01 · Context
The challenge
Personal project that started as a small Flask conjugation app and grew into a real platform, designed as if in production. Opi! does three things: it introduces new content (verbs, words, expressions) on a discovery sheet, it tests with typed exercises whose timing is planned by spaced repetition (FSRS), and it corrects by showing the expected form, the letters that differ and the rule. Each session mixes the reviews that are due, the exercises unlocked by the grammar skills and the day's new items, and the Library lets me browse all the content. That content is the central constraint: it lives in YAML files that I review by hand, only what I have approved reaches the exercises, and the code never invents a Finnish form. Personal use, not public, deployed as if in production.
02 · Architecture
How it's built
Key points
- Finnish content versioned in Git and reviewed by hand: the code never invents a form
- Four services (React front end, api, lexicon, srs) that only talk over JSON HTTP: none reads another one's database
- Reviews scheduled by the FSRS library (spaced repetition), without reimplementing the algorithm
- Infrastructure described as code (Terraform): three virtual machines and a k3s cluster that can be recreated identically
- Automated delivery: a merge on main rebuilds the images, stores them in a private Harbor registry, and Argo CD updates the cluster
- Site reachable only from the private network (Tailscale), with the progress backed up every night, restored and tested
03 · In pictures
Gallery

A session in progress: the present-tense table of a verb, to fill in form by form 
The Library, filtered by tag: the colours, each with its swatch 
The grammar skills, unlocked one by one (demo state) 
How a session is put together: due reviews, unlocked exercises, notions, then new items 
From an item to its cards, and the skills each card requires 
The content circuit: from a draft proposal to hand validation, then to review 
The architecture in detail: the ports, the rules that hold it together and the flows between services 
Login and security: two layers, a session cookie and checks on every request 
A card from end to end: the question, the corrected answer, the saved rating 
What the API returns at each session step, and the front-end screen that handles it 
A card's life in FSRS: learning, review and relearning 
The first version, local under docker compose, and what each v1 pull request changes 
From merge on main to the cluster: images rebuilt, pushed to Harbor, then Argo CD syncing in waves 
The Harbor image registry on the NAS, reached by two paths: the tailnet for CI, the local network for the nodes 
How CI joins the tailnet without a secret: a GitHub OIDC token verified by Tailscale 
The nightly backup of the srs database to the NAS, and the two ways to check or restore it 
Monitoring: Prometheus scrapes the services, two rules watch the backup, the UI stays on the tailnet
Click an image to open it full screen and browse through the screenshots.
04 · Takeaways
What I took away from it
Describing infrastructure as code (Terraform): the machines are recreated identically, and Argo CD brings the cluster back to the state described in Git. A merge is enough to deploy.