For the person who runs the server
The step-by-step setup instructions are in deploy/README.md in the SchoolStack code. This page is
an overview.
Installing and updating
- Install on Ubuntu 24.04 with
deploy/install.sh. With--domainand--emailit also sets up HTTPS;--time-zone Area/Citysets the school's time zone. - Update with
/opt/schoolstack/bin/schoolstack-update: it backs up, gets the latest code, builds, restarts, checks that the site works, and rolls back if it doesn't. - Check it is running:
systemctl status schoolstack, orcurl -s http://127.0.0.1:8080/healthz(it answers "ok" and the number of users).
Example: A teacher reports a fix is merged. The owner opens the DigitalOcean console, pastes
/opt/schoolstack/bin/schoolstack-update, waits for "Update finished", and opens the site on
their phone to check.
Administrators and the Users page
Administrators approve teachers and look after accounts. The first account created on a new server is its administrator. To make an existing account an administrator, paste this in the server's console (the site keeps running):
/opt/schoolstack/bin/schoolstack-make-admin you@example.com
Administrators see Users at the top of every page:
-
Add a person: name, email, and Student or Teacher (teachers added here can create classes at once). You get a temporary password, shown once at the top of the page; give it to them privately, and they choose their own password when they first sign in. (Command:
admin.addUser.) -
Add many people from a spreadsheet: one person per row, with the columns name, email, role, class code. Role is
studentorteacher(empty means student); the class code is optional and puts a student straight into that class. A first row with the column names is fine, in any order. Either choose a file (in Excel or Google Sheets: File ▸ Save as or Download ▸ CSV; choose CSV UTF-8 so Arabic and other names stay right) or copy the rows from your spreadsheet and paste them into Or paste the rows. Then Add everyone.- If any row has a mistake (a wrong email, an email that already has an account, an unknown class code), nobody is added and the page lists the rows to fix, so you can fix the spreadsheet and send it again.
- Otherwise the next page lists everyone with their temporary password, and Download
the list (CSV) saves it. Give each person theirs privately. Choose Done: I've saved the
passwords when finished; after that (or when you sign out) the passwords aren't shown
again. Up to 300 people at a time. (Command:
admin.addUsers.)
-
Teachers waiting for approval: people who chose "I'm a teacher". Approve as teacher lets them create classes; Make student turns the account into a student account. (Command:
admin.setRole.) -
All accounts: name, email, role, how many classes each teaches and learns in, and whether the account is on. For each person:
- Make teacher / Make student (Command:
admin.setRole.) - Make administrator / Remove administrator. There is always at least one
administrator. (Command:
admin.setAdmin.) - Turn off / Turn on: a turned-off account is signed out everywhere and can't sign in;
nothing in it is deleted. You can't turn off your own account. (Command:
admin.setDisabled.) - Reset password: a temporary password, shown to you once; they choose their own at their
next sign-in. (Command:
user.resetPassword.) - Students who signed up with a class code show "Signed up with the code for (class)".
- Make teacher / Make student (Command:
-
Who can create an account (at the bottom), then Save:
- Only people an administrator adds (school-level access): the sign-up page only says to ask the school's administrator. Add everyone with Add a person or from a spreadsheet.
- Only students with a class code: the sign-up page asks for a class code and makes a student in that class. Each student only gets in with a code a teacher gave out, and joins other classes only with their codes. Teachers get accounts only from Add a person.
- Anyone who can open this site (the starting choice): students and teachers sign up themselves; teachers wait for your approval.
The first account on a new server can always be created. The command-line tool isn't limited. (Commands:
admin.setSignUp;admin.signUptells which is chosen.) -
First day of the week (at the bottom): Saturday, Sunday (the starting choice) or Monday, then Save. Everyone's Calendar starts its weeks on that day. (Command:
admin.setWeekStart.)
Example: Mr. Ali signs up as a teacher. On Users you see him under "Teachers waiting for approval (1)" and choose Approve as teacher. A student who left school can be turned off. Another day you choose Only students with a class code and add the new science teacher with Add a person (Teacher); you read her the temporary password shown at the top.
Example: At the start of the year you choose Only people an administrator adds, save the student list from the school office's spreadsheet as CSV (Name, Email, Role, Class code), choose it under Add many people from a spreadsheet and Add everyone. Row 14 has a typo in an email, so nobody is added; you fix it, send it again, download the list of temporary passwords and hand them out in class.
Email (password-reset links)
With email on, people who forget their password get a link by email ("Forgot your password?" on the Sign in page) instead of asking you. SchoolStack sends through an email service; the steps below are for Brevo (free for up to 300 emails a day). Your site needs its domain first.
- In Brevo: Senders, Domains & Dedicated IPs ▸ Domains ▸ Add a domain, type your domain, and add the DNS records Brevo shows at your domain's DNS provider. Wait until Brevo says Authenticated.
- In Brevo: SMTP & API ▸ SMTP. Note the Login shown there, and choose Generate a new SMTP key. Keep the key private: don't send it to anyone or paste it into a chat.
- In the server's console, run
/opt/schoolstack/bin/schoolstack-email-setup. Type the login, then paste the key (it doesn't show as you type) and press Enter. The site restarts with email on, sending asno-reply@your domain. - On Users ▸ Email, choose Send me a test email. It arrives within a minute or two (look in spam the first time). If it fails, the page says why.
Good to know: SchoolStack uses port 2525, because DigitalOcean blocks the usual email ports on
new accounts. Another service works too: /opt/schoolstack/bin/schoolstack-email-setup --host smtp.example.com --port 587. To turn email off: /opt/schoolstack/bin/schoolstack-email-setup --off. The settings live in
/etc/schoolstack/schoolstack.env (readable by root only), and re-running the installer keeps
them.
Example: After Brevo shows schoolstack.me as authenticated, the owner runs
/opt/schoolstack/bin/schoolstack-email-setup, pastes the key, and sends a test email from Users. From then on a
student who forgets their password resets it themselves.
Daily summary email
With email on, everyone gets one email a day at 7:00 (the school's time zone) with what's new in
their classes: new work, work returned and work due tomorrow for students; work turned in for
teachers. People with no news that day get nothing. Users ▸ Email ▸ Choose what each class
sends opens Class emails (/admin/emails): every class with its four switches and Save
email choices; the class's teachers can change the same switches in the class's Settings.
(Commands: class.setEmails; admin.classEmails lists every class's choices.)
Guardian summaries go out at the same time: parents or guardians that teachers (or you) invited on a class's People page get a summary of their student's missing and upcoming work and class news, daily on school days (the school's week minus its last two days) or weekly on the first day of the school's week (Users ▸ First day of the week). You can invite guardians of any student. They count towards the 250 a day.
- Users ▸ Guardian summaries: On (the starting choice) or Off, then Save. Off,
nobody can be invited and no guardian summary is sent; guardians already invited are kept for
when you turn it on again. (Command:
admin.setGuardians.) - Invite guardians from a spreadsheet (under it, while it's on and email is on): one row per
guardian, the student's email, the guardian's email (a first row "Student email, Guardian
email" is fine, in any order); choose the CSV file or paste the rows, then Invite these
guardians. If any row is wrong (no student with that email, the student's own email, already
a guardian, a row twice), nobody is invited and you see which rows to fix. Each guardian gets
the invitation email. At most 300 rows at a time. (Command:
admin.inviteGuardians.)
Example: At the start of the year the school office has a spreadsheet of students and their parents' emails. You save it as CSV with the two columns, choose it under Invite guardians from a spreadsheet, and every parent gets an invitation that morning.
Good to know:
- Brevo's free plan sends 300 emails a day. SchoolStack sends at most 250 summaries a day, leaving room for password-reset links and invitations; if more people have news, the rest miss that day's summary (the server's log says so).
- Each summary covers the time since the last one (at most two days back after the server was off). If an email can't be sent, it isn't retried; the next day's summary carries on.
- Addresses ending in
.test,.example,.invalidor.localhostnever get it.
Example: The science department only wants students emailed, so on Class emails the owner turns off "Teachers: work students turned in" for the three science classes and saves each.
Test accounts
For testing (by hand, or by a testing service such as TestSprite), run in the server's console:
sudo /opt/schoolstack/bin/schoolstack-test-accounts
and type a password (it isn't shown). It makes test-admin@schoolstack.test (administrator and
teacher), test-teacher@schoolstack.test, test-student1@schoolstack.test and
test-student2@schoolstack.test, all with that password; running it again sets the password
again. It also makes, once, the class "Test class: returned work" (taught by test-teacher, with
both test students in it and a returned essay with a grade, rubric feedback and a private
comment), so testers can check what students see after grading.
When testing is done, sudo /opt/schoolstack/bin/schoolstack-test-accounts --off turns them
off. The .test addresses never receive email. The product requirements for testers are in
docs/testing/ in the repository.
Example: Before handing the site to a testing service, the owner runs the command, types a
password, and gives the service https://schoolstack.me, test-teacher@schoolstack.test and that
password.
Where the data lives
- Accounts and school settings:
/var/lib/schoolstack/school.json; each class (its posts, work and grades) in its own file in/var/lib/schoolstack/school-classes/, so a change rewrites only the class it touches and the site stays quick for a whole school. They're plain, readable text./opt/schoolstack/bin/schoolstack-cli --data /var/lib/schoolstack/school.json exportprints all of it as one file (that's what the backups hold). - Uploaded files:
/var/lib/schoolstack/files, each named by its contents' fingerprint (SHA-256), so a file used in many places is stored once. - Who is signed in:
/var/lib/schoolstack/logins.json, so updates and restarts don't sign anyone out. It holds only a fingerprint of each sign-in, never anything that could be used to sign in. Deleting it (with the site stopped) signs everyone out. - Backups:
/var/backups/schoolstack, every night and before every update, 14 kept, plus every uploaded file. - Copies off the server: download the backups to your own computer now and then (once a week is a
good habit). The one-time key setup and the download command are in
deploy/README.md, "Download the backups to your computer".
Example: A teacher deleted a class by mistake yesterday. The owner finds last night's backup in
/var/backups/schoolstack and follows the restore steps in deploy/README.md.
Server options
Set them in /etc/schoolstack/schoolstack.env as SCHOOLSTACK_FLAGS=…, then
systemctl restart schoolstack:
--time-zone Area/City: the school's time zone (for exampleAsia/Riyadh), for everyone who hasn't chosen their own.--max-upload-mb N: the largest file people can upload (25 by default). Also raisemax_sizein/etc/caddy/Caddyfilea little above it.--files-quota-mb N: the space all uploads may use together (10 GB by default).--secure-cookies: set by the installer when the site uses HTTPS.
With a domain, www.(your domain) sends people to the address without www (for example
www.schoolstack.me → schoolstack.me). It needs its own DNS record: a CNAME to your domain, or
an A record to the server. schoolstack-update adds it to servers installed before it existed.
Example: Teachers want to upload longer videos, so the owner sets
SCHOOLSTACK_FLAGS=--time-zone Asia/Riyadh --max-upload-mb 50, raises max_size in the Caddyfile
to 55 MB and runs systemctl restart schoolstack.
Command-line tool and AI agents
schoolstack-cli runs the same actions as the web pages, as a chosen user. While the site runs,
it sends each change to the running server through /var/lib/schoolstack/school.sock (only root
and the schoolstack account can use it, so run the tool with sudo), so nothing needs to stop;
when the site is stopped, it changes the data files itself.
schoolstack-cli --data FILE adduser "Name" email "password"creates an account.schoolstack-cli --data FILE set-password email "password"gives an account a new password.schoolstack-cli --data FILE make-admin emailmakes an account an administrator (on the server, useschoolstack-make-admin).schoolstack-cli --data FILE exec --as email ACTION '{json}'runs an action, for exampleexec --as tess@example.org class.create '{"name":"Art"}'.schoolstack-cli --data FILE exportprints the whole school as one file (the backups use it).schoolstack-cli --data FILE mcpstarts the MCP server, so AI agents can use every action.command.listandcommand.describe(queries) list the actions and their parameters.
Every action is listed in the reference.
Example: To make a class for a new teacher without signing in, the owner runs
sudo schoolstack-cli --data /var/lib/schoolstack/school.json exec --as tom@school.org class.create '{"name":"Physics"}';
Tom sees "Physics" on his Classes page straight away.