Lukas Beierlieb · 9 min read
NixOS for Programming-Exam Environments
What if your keyboard locked itself the second the exam ended? At the University of Würzburg, programming exams got a major tech upgrade thanks to NixOS—bringing reproducible setups, secure boot, and remote exam control into the classroom.

Every six months, just before the start of a new semester, the Chair of Software Engineering at the University of Würzburg organizes exams for the programming practical course (known in German as “Programmierpraktikum”, or “PP” for short). In contrast to most other exams, the PP exams are not based on paper but require the students to solve a coding task on computers. In 2024, new hardware in the computer pools initiated a switch away from the old Ubuntu-based images to NixOS.
We published numerous blog posts that explain how our company benefits from using Nix and NixOS in our engineering. In this post, we are happy to showcase the value NixOS brings to the University of Würzburg. We discuss why the IT department switched the pool computers to NixOS, how NixOS allowed the PP organizers to improve the exam execution, and how Cyberus contributes.
The Switch to NixOS
The exams take place in the computer pools that are usually publicly accessible for students. For this general use, the IT department locked the previous generation of pool computers down—students could choose between the installed Windows and Ubuntu, but they could not launch operating systems from USB sticks. In 2024, IT replaced the computers with newer models, which had no configuration option to prevent booting from external media.
The solution: Configure Secure Boot, such that user-brought operating systems cannot boot. However, initial investigations of the IT department concluded that realizing the Secure Boot configuration on the existing Ubuntu images was a more involved undertaking than achieving the same configuration on NixOS. So, the final decision was the switch to NixOS and lanzaboote—a project enabling Secure Boot for NixOS, amongst others developed and maintained by Cyberus employees.
Image: A computer pool at Uni Würzburg, where PP exams take place.
New Opportunities for the Exam Environment
The exam environment was subsequently also switched to NixOS. This change made adapting the exam environment significantly more comfortable. Previously, the IT department had to boot the current Ubuntu image, apply changes carefully (to not accidentally modify the system state in an undesired way), and then generate a new image. With NixOS, a configuration file defines all installed packages, running services, and other OS settings. Need another package installed or an open port in the firewall? Just adapt the configuration file, build the system configuration, and remotely deploy it to all pool computers!
With the cumbersome process to adjust the Ubuntu image, the IT was hesitant about integrating non-essential features the PP organizers wished for. So, the introduction of NixOS allowed the PP organizers to address two problems identified in the previous exams:
- Students scrolled to the exam task description before the exam started (they are supposed to read only the general notes at the top of the document).
- Students continued typing even after the time was over—two supervisors cannot keep twenty students in check.
Hiding the Task Description Before the Exam
The pool computers do not store any user information and files, instead they rely on LDAP and NFS. The students in the exam use their matriculation number to log into their respective exam user account. Such an account has read access to the task description and write access to the project template, where students have to add their code. Previously, general hints (students can read before the exam) and the task description (students are not allow to read before the timer starts) were in the same file and thus the task description was just one scroll away.
Now, the hints and the task description lie in the NFS share as separate files, as well as a symbolic link, which initially points to the hints. The students open the symlink via a desktop shortcut in the browser. At the same time as starting the 90-minute exam timer, the exam supervisor points the symlink in the NFS share (thus, for everybody) to the task descriptions; students only need to refresh their browser.
Locking Keyboards After the Exam
Reliably preventing further input after the time is up is key to ensure an orderly end of the exam. kanata is a keyboard-remap tool, allowing users to configure their keyboards with custom behavior. The exam kanata configuration defines two keyboard layers. The enabled layer maps each key to its own key code—the keyboard behaves exactly as without kanata. The disabled layer maps each key to perform no action. kanata can also spin up a TCP server and accept layer-change commands over the network. This feature was introduced, so that window managers could inform kanata about the currently selected application and enable application-specific key bindings. However, the exam supervisors can utilize the functionality to remotely switch all student keyboards from enabled to disabled when the time is up.
kanata is available—with around 125.000 other packages—in the official Nixpkgs repository. Further, NixOS even provides a module to run kanata as a service. Have a look at the NixOS configuration snippet that enables kanata with a TCP server and opens the associated port in the firewall:
services.kanata = {
enable = true;
keyboards.default.configFile = ./kanata.cfg;
keyboards.default.extraArgs = [ "-p" "0.0.0.0:5000" ];
}`
networking.firewall.allowedTCPPorts = [ 5000 ];
Additional Requirement: Exact Countdown
The keyboard locking and the requirement that the students’ final code has to compile created a new issue that was not as present before: Students have to know exactly how much time is left, whereas previously supervisors manually notifying from time to time was enough. Announcing “one minute until your keyboards stop working, and exactly then your code has to compile” is not fair and would probably lead to students quickly fixing up the current line, before waiting the final 50 seconds in safety. A timer that shows the remaining time down to the last second allows students to make full use of the last minute as well.
During the pre-exam announcements, when the students open the general hints in the browser, the desktop shortcut opens also another tab, which is configured to retrieve the current exam timer state (the exam supervisor controls this information) and display the remaining time.
The Control Center: pp_klausur_manager
Remotely switching keyboard layers, changing symlinks in NFS, providing timer information; how does the supervisor do all these tasks during the exam, while also keeping an eye on the students? The Rust application pp_klausur_manager (available on GitHub), developed and maintained by a Cyberus employee, implements the required functionality and enables the supervisor to control the exam flow with single key presses.
Image: pp_klausur_manager running on a pool computer.
At the top, the applications terminal UI shows the timer information. Once started, the time can be adjusted (e.g., to give a few minutes extensions if problems arise during the exam). In the background, a web server provides the timer info to the student computers.
The next line shows the state of the task description symlink. The application automatically changes the symlink when the timer is started; it presents the path to assure the supervisor everything worked as expected.
At the bottom, a list presents status information of all student computers. The first column shows the active kanata keyboard layer. Pressing space (when the time is up) transmits a switch-to-disabled message to everybody; pressing escape sends switch-to-enabled messages. Further, the list displays for each student computer how long ago the student’s timer display retrieved the timer status (every three seconds before the timer starts; every thirty seconds once started, to update in case of time extensions). The kanata and timer access data allow the supervisor to monitor any deviations from the intended behavior.
The Value of Testing with nixosTest
The correct behavior of the pp_klausur_manager, student timer display, and kanata configuration is absolutely critical to ensure a smooth exam execution. Testing in the computer pools on the actual hardware is ideal, but it is not always feasible. The IT department has to coordinate the infrastructure configuration, and the pools are meant to be publicly accessible student work spaces, so, room reservations are required. The team performs a complete dry run the day before the exam, but for development such testing is not an option.
nixosTest is a feature of Nix that allows building a virtual machine or a network of virtual machines and running a test script of commands (such as starting a machine, waiting for a system service to come up, confirming the successful execution of a command). Automating tests for a terminal-UI application and keyboard remapping is not trivial; however, nixosTest also allows the tester to interactively control the virtual machines. In this case, manual testing in a network of virtual machines is good enough.
The test definition is not complex either, because the test connects all nodes with a virtual network by default:
{ pkgs, pp_klausur_manager }:
pkgs.nixosTest {
name = "virtual exam computer pool";
nodes = {
controlmachine = import ./config_controlmachine.nix;
client1 = import ./config_client.nix;
client2 = import ./config_client.nix;
client3 = import ./config_client.nix;
};
test_script = ''
controlmachine.start()
controlmachine.wait_for_unit("nfs-server.service")
start_all() # ignores controlmachine, because it is already started
''
}
From any system (with the Nix package manager and KVM virtualization available), you can run
nix run github:lbeierlieb/pp_klausur_manager#checks.x86_64-linux.virtual-exam-computer-pool.driverInteractive
to get into the interactive python test shell. Type test_script()
to invoke the script (defined in the previous listing) and start the virtual machines.
Image: A virtual computer pool for quick and easy testing.
Rebuilding and booting all virtual machines takes just above a minute on a Framework 13 laptop with an AMD R5 7640U processor.
Ready to Build with Nix?
Nix and NixOS are powerful tools to declaratively configure systems for all kinds of use cases. The extensive test tooling allows for easy verification of the resulting system. Cyberus’ Nix experts can help you get started on your own journey: Amongst other projects, we have packaged OpenStack for NixOS, helped customers build an edge gateway platform for the medical space, and improved testing of passport readers.
Do you need help building a system tailored to your needs? Schedule a free evaluation session with us to get started on the solution you need.