ADR-008: Use MinIO for Object Storage
Status
Accepted
Context
Chilarai needs a storage solution for managing binary assets and documents.
Examples include:
- Student Photos
- Staff Photos
- Admission Documents
- Transfer Certificates
- Report Cards
- School Assets
The solution should:
- Be open source
- Support self-hosted deployments
- Run easily in containers
- Be cloud portable
- Avoid vendor lock-in
- Support secure document access
- Work for both SaaS and on-premises deployments
The application already stores document metadata in PostgreSQL. Binary content should not be stored in the database.
Decision
Chilarai will use MinIO as the default object storage provider.
The application will access storage through a StorageProvider abstraction.
The initial implementation will use MinIO.
Future implementations may use:
- Amazon S3
- Cloudflare R2
- Azure Blob Storage
without requiring changes to business logic.
The application will store object keys and metadata in PostgreSQL.
The application will not store binary file content in PostgreSQL.
Rationale
Open Source
MinIO is open source and can be self-hosted without dependency on a cloud provider.
Container Friendly
MinIO runs easily in:
- Docker
- Kubernetes
- Virtual Machines
This aligns with Chilarai's self-hosting goals.
Cloud Portability
MinIO provides S3-compatible APIs.
This enables future migration to:
- Amazon S3
- Cloudflare R2
- Other S3-compatible providers
with minimal code changes.
Vendor Neutrality
The StorageProvider abstraction prevents storage vendor lock-in.
Business modules remain independent of storage implementation details.
Security
MinIO supports:
- Private Buckets
- TLS
- Encryption at Rest
- Pre-Signed URLs
This allows secure access to student and school documents.
Storage Model
Database
Store:
- ObjectKey
- FileName
- ContentType
- FileSize
Do not store:
- Images
- PDFs
- Binary Documents
inside PostgreSQL.
Access Pattern
Files will be stored in private buckets.
Applications will access files through temporary pre-signed URLs generated by the StorageProvider.
Example Object Keys
student-photos/student-123/profile.jpg
admission-documents/admission-456/birth-certificate.pdf
Consequences
Positive
- Open source
- Self-hostable
- Cloud portable
- Vendor neutral
- Container friendly
- Secure by default
- Suitable for both SaaS and on-premises deployments
Negative
- Additional service to operate
- Backup and storage management become operational responsibilities
- Monitoring and maintenance required for self-hosted deployments
Alternatives Considered
Amazon S3
Rejected as the default implementation due to cloud dependency and vendor lock-in concerns.
May be supported later through the StorageProvider abstraction.
Azure Blob Storage
Rejected as the default implementation due to vendor dependency and reduced portability.
May be supported later through the StorageProvider abstraction.
Cloudflare R2
Rejected as the default implementation because it is a managed service and not self-hostable.
May be supported later through the StorageProvider abstraction.
Local Filesystem Storage
Rejected because it does not scale well and complicates backup and multi-instance deployments.
Implementation Notes
Module structure:
<module>
├── domain
├── application
├── data
└── api
Storage implementations belong in the Data layer.
Example:
shared/data/storage
├── storage-provider.ts
└── minio-storage-provider.ts
Business modules should depend only on StorageProvider.