Devora Cloud · Blog
Private S3 for your app: keep images and files out of the database
Why images, PDFs and videos belong in S3 with only the key in the database, how Devora Cloud’s private per-project bucket works, signed URLs, included storage and extra packs.
Every app starts small: a form with a photo upload, a field to attach a PDF. The quickest fix is usually to write the file to the server disk or straight into a database column. It works on day one — and becomes a problem at the first deploy, the first replica or the first giant backup.
This guide explains why files should live in S3 object storage, how each project’s private S3 works on Devora Cloud, and how to show those files to visitors without making anything public.
Why not store files in the database or on disk
A database is great for structured information: users, orders, prices. Storing image and video binaries in tables makes the database grow fast, slows down backups and restores, and competes for memory with the queries that matter.
The container disk has a different problem: it is neither permanent nor shared. A new deploy creates a clean environment, and load-balancing replicas cannot see each other’s disks. A file uploaded to one copy simply does not exist on another.
The practical rule is simple:
- The file goes to S3.
- The database keeps only the reference: the object key (for example, products/123/photo.png), the original name, size, type and owner.
- The application generates the link when it needs to display the file.
One private bucket per project, with its own key
On Devora Cloud, each project can have its own private S3 bucket with its own access key. That key only works on that bucket: one project cannot read or delete another project’s files, even inside the same account.
When storage is created, the project receives ready-to-use environment variables:
- S3_ENDPOINT=https://s3.devoracloud.com
- S3_REGION=us-east-1
- S3_BUCKET, with the project bucket name
- S3_ACCESS_KEY and S3_SECRET_KEY
- S3_FORCE_PATH_STYLE=true
Any S3-compatible SDK works — AWS SDK v3 in Node.js, boto3 in Python, Laravel’s S3 driver and others — as long as it uses path-style addressing. Inside the bucket you can create any folders you like, such as uploads/2026/ or invoices/.
Credentials must only be read on the server. Never put the secret key in variables exposed to the browser, such as those starting with NEXT_PUBLIC_.
Signed URLs: show files without making them public
If the bucket is private, how does a visitor see a product photo? With a signed (presigned) URL. The application generates, on the server, a temporary link to one specific object — valid for an hour, for example. Whoever has the link can download that file until it expires; after that, the link stops working.
- Product photos and avatars: generate the URL while rendering the page.
- Invoices, contracts and medical records: generate the URL only after checking that the signed-in user is allowed to see the file.
- Your site’s own logo, CSS and icons: ship them with the code, not in the bucket.
The access key never reaches the browser. The visitor only receives the signed link, which expires.
How much storage is included
| Plan | Monthly price | Included S3 |
|---|---|---|
| Hobby | US$ 5 | 1 GB |
| Pro | US$ 20 | 5 GB |
| Enterprise | US$ 59 | 25 GB |
Storage belongs to the account, not to each project: it is shared by all projects in the account. If you need more, buy an extra pack in the dashboard:
- 100 GB for US$ 3 per month
- 250 GB for US$ 4 per month
- 1 TB for US$ 12 per month
Paid packs add to your plan’s limit. Payment is by card, or by Pix in BRL for customers in Brazil.
What happens when storage is full
When usage reaches the limit, new uploads are rejected (S3 answers with a QuotaExceeded error). Files already stored stay safe and accessible; nothing is deleted.
To upload again, delete files you no longer need, buy a pack or move to a plan with more space. Handle this error in your code and show users a clear message instead of a generic failure.
If you use the AI token, your AI checks S3 usage before uploading many files and warns you when it goes above 80%.
Checklist for moving uploads to S3
- Create the project’s S3 storage in the dashboard, or ask your AI to do it.
- Replace disk writes with uploads to S3 using the project variables.
- Add a database column for the object key and stop storing binaries in tables.
- Generate signed URLs when displaying private files.
- Copy existing files over: adding S3 does not migrate what is already there.
- If your plan allows it, test with two replicas to confirm nothing depends on the disk.
Frequently asked questions
Does Devora’s S3 work with AWS SDKs?
Yes. It speaks the S3 API, so it works with common SDKs (AWS SDK, boto3, Laravel and others) once you set the endpoint https://s3.devoracloud.com and path-style mode.
Can I make a file public?
The bucket is private. To show files to visitors, generate temporary signed URLs in your application; never expose the access key.
Is plan storage per project?
No. Included storage (1, 5 or 25 GB) and extra packs are shared by all projects in the account.
Do I lose files if storage runs out?
No. Only new uploads stop. Stored files stay there until you free up space or raise the limit.