Deployment
One host, thirty minutes, your infrastructure
CampusLink is a single-tenant application that runs on a server you control. No container orchestration to learn, no cloud account to open, and no vendor holding a copy of your student data.
Architecture
Deliberately unremarkable
nginx terminates TLS and proxies to a Next.js process on localhost. A second PM2 process ticks every five minutes for recurring supply orders, footage retention scrubbing and webhook retries. MariaDB sits on the same host.
This is a stack a district technology department can already operate. Horizontal scaling is a later problem — for the size of district CampusLink is built for, one properly specified host is the correct answer, and adding a cluster would add failure modes without adding capacity anyone needs.
Attachments — permit PDFs, insurance certificates, footage extracts, safety plans — live on local disk with metadata in the database, or in an S3 bucket if you would rather. Everything else in the deployment is reproducible from a git pull and a build.
Requirements
What to provision
Hand this section to whoever builds your servers. There is nothing unusual on it.
| Operating system | Ubuntu 22.04 LTS or 24.04 LTS, x86_64 |
| Minimum | 2 vCPU · 4 GB RAM · 50 GB disk |
| Recommended | 4 vCPU · 8 GB RAM — sized for a district of about 20,000 students |
| Runtime | Node.js 20 · Next.js 14 · PM2 (web process + background worker) |
| Database | MariaDB or MySQL 8, local to the host |
| Web | nginx terminating TLS, reverse-proxying to 127.0.0.1 |
| Network | 80/443 inbound; outbound 443 to your SMTP relay and SAML IdP |
| Attachments | Local disk at /var/lib/campuslink/attachments, or an S3 bucket |
A SAML 2.0 identity provider — required
It must issue an SP-initiated response, sign with RSA-SHA256, use
the email NameID format, and emit email,
displayName and groups. Known good: Entra
ID, Okta, Google Workspace, OneLogin, JumpCloud.
An SMTP relay — strongly recommended
Approvals, receipts and notifications flow through it. Without one the platform still boots, and messages are recorded in the email log as skipped rather than silently lost — but nobody receives them.
Installing
Three ways in, one result
Scripted
One idempotent shell script on a clean Ubuntu host. Installs Node, the database, nginx and certbot; creates the database and a random password; generates the environment file with a fresh secret; sets up both PM2 processes; runs migrations and seeds.
Manual
Every step run by hand, for locked-down environments, non-Ubuntu distributions or VM templates. Same result. Districts whose security team wants to watch each step usually choose this one — and the script is heavily commented precisely so it can be read first.
Proxmox
A script on the PVE host creates an LXC container, picks the next free cluster-wide ID, and runs the manual install inside it.
Upgrades
A customer is a profile, never a branch
Every district runs the same code, unmodified. What makes an install yours is a small profile file and an environment file — your name, your buildings, your branding, your SAML details.
This is the single most important thing about the upgrade path. The moment district-specific behaviour is committed into a module, that district needs a fork, and a fork stops receiving upstream fixes without a merge somebody has to pay for. If a requirement cannot be expressed in the profile, the profile is extended — for every district at once.
The practical result: a security fix released on a Tuesday reaches every district on the same Tuesday, and no district is quietly stranded three versions back because of a customisation made two years ago.
// everything that makes an install yours
export const profile: CustomerProfile = {
key: 'riverview',
districtName: 'Riverview Community Schools',
emailDomain: 'riverview.k12.in.us',
publicHost: 'campuslink.riverview.k12.in.us',
sites: [
{ code: 'DIST', name: 'District Office', kind: 'DISTRICT' },
{ code: 'RHS', name: 'Riverview High School', kind: 'SCHOOL' }
],
branding: { primaryColor: '#1d4ed8' }
}; Managed operation
Or let us run it
Self-hosting means the district owns the server, the backups, the certificates and the uptime. Plenty of technology departments are glad to. For those that would rather not add another system to the on-call list, Melton Electronics operates it on your infrastructure.
- Install and initial configuration on your hardware or VM
- SAML wiring against your identity provider, with the claim-to-role rules written for your org structure
- Site, room and building import from whatever export you can produce
- Version upgrades scheduled outside school hours
- Backup verification and periodic restore drills — the part everyone plans and nobody tests
- Named contact with a response commitment in writing
What the district still owns
Even under a managed agreement, these stay with you — and any vendor who tells you otherwise is describing something they cannot deliver.
- The server or VM, and the network it sits on
- The data, and the retention policy applied to it
- Identity provider configuration and account lifecycle
- Staff training and the directory-information policy
Have your technology director on the call
The deployment conversation goes faster with the person who will run it in the room. Bring your IdP, your MDM and your backup arrangement, and we will map it out live.