---
title: "Discussion on \"From Bare-Metal Infrastructure to Multi-Tenant SaaS: Architecting a Django Platform for 1,800+ Users\" | Hashnode"
description: "Discussion on \"From Bare-Metal Infrastructure to Multi-Tenant SaaS: Architecting a Django Platform for 1,800+ Users\". How nine years of managing physical networks, power failures, and Linux servers shaped the backend architecture of EduSleek."
url: "https://hashnode.com/posts/architecting-multi-tenant-django-saas-1800-users/6ad4e492097b8b8914fdac8b"
---

[Feed](https://hashnode.com/feed) /

Discussion

# [From Bare-Metal Infrastructure to Multi-Tenant SaaS: Architecting a Django Platform for 1,800+ Users](https://ebubemba.hashnode.dev/architecting-multi-tenant-django-saas-1800-users?utm_source=hashnode&utm_medium=feed)

How nine years of managing physical networks, power failures, and Linux servers shaped the backend architecture of EduSleek.

Oct 18 · 5 min read · ebubemba.hashnode.dev  
Read the article

-   [#python](https://hashnode.com/tag/python)
-   [#django](https://hashnode.com/tag/django)
-   [#postgresql](https://hashnode.com/tag/postgresql)
-   [#docker](https://hashnode.com/tag/docker)
-   [#backend-developments](https://hashnode.com/tag/backend-developments)
-   [#system-design](https://hashnode.com/tag/system-design)

## Written by

[Ebube Mba](https://hashnode.com/@techgoodness)

@techgoodness · Abuja, Nigeria

  
[View profile](https://hashnode.com/@techgoodness)

Backend-focused Full-Stack Python Engineer and Systems Administrator with 9 years in IT operations and 5+ years engineering production software. Architected and operates EduSleek, a multi-tenant SaaS platform (Django, PostgreSQL, Redis/Celery, HTMX, Tailwind CSS, Docker) actively serving 1,800+ users on Hetzner cloud infrastructure with Coolify. Proven expertise in asynchronous background processing, dynamic JSON schema design, Linux server hardening, and shipping production LLM features via the Groq API. Combines deep infrastructure troubleshooting with modern AI-augmented development workflows to deliver resilient, scalable systems.

## Responses 4

Markdown supported · ⌘↵ to post

[AI Developer](https://hashnode.com/@lycore)

[Oct 26](https://hashnode.com/posts/architecting-multi-tenant-django-saas-1800-users/6ad4e492097b8b8914fdac8b/comment/6adf88feced913dfcfc7129f)

One thing I'd probe in the JSONB blueprint approach is versioning: if a tenant edits its rubric mid-term, previously validated scores could be silently reinterpreted against the new rules. Storing a blueprint version (or a snapshot of the rules) alongside each score payload would keep historical report cards reproducible. Also, with JSONB you lose DB-level constraints, so have you considered a CHECK constraint or GIN index on the keys you filter/rank by most often?

[Ebube Mba](https://hashnode.com/@techgoodness)

[Oct 26](https://hashnode.com/posts/architecting-multi-tenant-django-saas-1800-users/6ad4e492097b8b8914fdac8b/comment/6adf88feced913dfcfc7129f#reply-6adf9eb053ea3fe1a14f3d54)

Thanks for the thoughtful feedback! You hit right on some of the biggest tradeoffs we had to weigh when choosing JSONB.

Regarding the mid-term rubric edits: you are 100% right that arbitrary tenant edits would break historical data. To protect against this, we actually don't expose the blueprint configuration to the school admins in the UI. The assessment blueprint is configured exclusively by our dev team during onboarding. Schools don't change their grading rubrics arbitrarily and if they do need a structural overhaul, it's handled as a supervised, scheduled migration by the engineering team between academic sessions, precisely because it is a destructive action.

That being said, I really love your idea of storing a blueprint version (or a lightweight snapshot hash) alongside the Result payload. Even with dev-controlled blueprints, a school might legitimately change their policy from a 4-tier to a 5-tier assessment system three years from now. A version stamp would guarantee that historical report cards remain mathematically reproducible decades later without needing complex temporal tables.

On the database constraints and GIN indexing: this is the classic JSONB tradeoff! Because we lose DB-level schema enforcement, we rely heavily on application-level validation (Django forms/validation logic) to strictly sanitize the payload before insertion. As for indexing, we intentionally lift the most heavily queried and ranked data out of the JSON. For example, total\_score and average\_score are explicitly stored as DecimalFields on the Result model, allowing us to use standard B-Tree indexes and native ORM aggregations. The JSONB field is primarily treated as a storage/presentation mechanism rather than a deep query target, though adding a GIN index is absolutely the next logical step if we ever need to run analytics on specific sub-scores (like isolating 'homework' trends across a district).

Really appreciate the insights. Definitely adding the versioning snapshot idea to the roadmap!"

[AI Developer](https://hashnode.com/@lycore)

[Oct 27](https://hashnode.com/posts/architecting-multi-tenant-django-saas-1800-users/6ad4e492097b8b8914fdac8b/comment/6adf88feced913dfcfc7129f#reply-6adfeb512420fc49f43975a0)

Keeping the ranked fields like total\_score and average\_score as DecimalFields while JSONB stays a storage/presentation layer is a clean split, and it makes the B-Tree indexes do real work. One small addition if you adopt the version stamp: a CHECK constraint or a validator that rejects payloads whose blueprint version doesn't match a known one would catch drift early, even without schema enforcement on the keys. Thanks for the detailed answer.

[Ebube Mba](https://hashnode.com/@techgoodness)

[Oct 18](https://hashnode.com/posts/architecting-multi-tenant-django-saas-1800-users/6ad4e492097b8b8914fdac8b/comment/6ad4e5fb2dc0fc02e8bc414f)

A testament to your unwaivering dedication and commitment to providing solutions. This is a great solutions that relieves schools of administrative and academic headache
