Every application that accepts photos or videos eventually faces the same question: should the files stay on the server that runs the application, or move to a storage service such as Amazon S3, Google Cloud Storage, or Bunny Storage? There is no single right answer. A small internal system can run for years with files on its own disk, while a marketplace with thousands of product photos or a learning platform full of videos usually outgrows it within months. Below we compare both approaches, look at the main providers, and run the numbers for a typical case.
Two Very Different Ways to Keep Files
Saving to the server disk means the application writes the upload into a folder such as storage/uploads, and the web server (Nginx, Apache, or the framework itself) sends the file back when someone asks for it. Storage, delivery, and backup are all your responsibility, and they share CPU, memory, disk, and network with the application.
Cloud storage, more precisely object storage, is a separate service that you talk to over an API. The application uploads a file and gets back a key, and users download the file from the storage service or from a CDN in front of it. The provider takes care of disks, replication, and capacity. You pay per gigabyte stored, per request, and, depending on the provider, per gigabyte downloaded.
Side-by-Side Comparison
| Aspect | Server disk | Cloud storage |
|---|---|---|
| Initial cost | Already included in the VPS or server price | Pay per use, often a few dollars a month to start |
| Capacity | Fixed by the plan. When the disk is full, uploads fail and the database can stop too | Grows automatically, and you pay for what you actually store |
| Durability | One disk or one RAID array. Safety depends on your backups | Copies across several disks or facilities, for example S3 is designed for 11 nines of durability |
| Bandwidth to users | Limited by the server port and quota. Large videos compete with API traffic | Delivered by the provider or a CDN, separate from the application server |
| More than one server | Needs shared storage such as NFS or file syncing between servers | Every server reads and writes the same bucket |
| Backup and restore | Hundreds of gigabytes to copy every night, and slow to restore | Versioning, replication, and lifecycle rules are built in |
| Monthly bill | Predictable, since it is part of the server cost | Changes with usage, especially download traffic |
| Effort for the team | Simple at first, more operational work as files grow | A little more work at first: credentials, signed URLs, CORS |
When the Server Disk Is Still a Good Choice
- Internal applications with a limited number of users, such as an HR or inventory system, where files are mostly PDFs and a few photos.
- Total files that will stay in the tens of gigabytes for the next one or two years.
- The application runs on a single server and there is no plan to add more soon.
- Rules or contracts that require data to stay on hardware owned by the company, for example an on-premise system in a factory or a hospital.
- There is already a tested backup that copies the upload folder to another location, not only to the same server.
Even in these cases, write the code through a storage abstraction such as the Laravel Storage facade, django-storages, or a small storage interface of your own. The application then saves and reads files without knowing where they live, and moving to S3 or another provider later becomes a configuration change rather than a rewrite.
Problems That Appear as Photos and Videos Grow
- The disk fills up quietly. Logs, the database, and uploads usually share the same partition, so a full disk stops much more than file uploads.
- Backups get slow and expensive. A 300 GB upload folder takes hours to copy, and restoring it after a failure keeps the application offline just as long.
- Videos use the bandwidth meant for the application. A VPS port of 100 Mbps or even 1 Gbps fills up quickly when dozens of users watch videos at the same time, and API responses start to slow down for everyone.
- Application workers are tied up. If files go through PHP, Node.js, or Python instead of being served directly by Nginx, every download holds a worker for the whole transfer.
- Adding a second server becomes complicated. Files uploaded to server A are not on server B unless you set up shared storage or syncing.
- Security mistakes are easier to make. An upload folder inside the public web root that allows scripts to run is a classic way for an attacker to upload a web shell.
The Main Cloud Storage Options
Most of the services below support the S3 API, so the same SDK and tools work with all of them by changing the endpoint and credentials. The prices are list prices in US dollars as of November 2026 and do not include tax.
| Service | Storage price | Download to the internet | Notes |
|---|---|---|---|
| Amazon S3 (Jakarta) | $0.025 per GB per month | $0.132 per GB after the first 100 GB free each month | The most complete feature set. Usually paired with CloudFront for delivery |
| Google Cloud Storage (Jakarta) | $0.023 per GB per month (Standard) | Billed per GB, with Indonesia in a more expensive price band than most of Asia | A natural choice if the application already runs on Google Cloud |
| Cloudflare R2 | $0.015 per GB per month, first 10 GB free | Free | Request fees still apply: $4.50 per million writes and $0.36 per million reads |
| Bunny Storage | $0.01 per GB per month for one region (HDD), $0.02 for SSD | Through Bunny CDN, $0.03 per GB in Asia on the standard network | No API fees, a $1 monthly minimum, and a Singapore storage region close to Indonesia |
| Backblaze B2 | $6.95 per TB per month | Free up to 3 times the data stored, then $0.01 per GB | Low storage price, a good fit for archives and backups |
| Wasabi | $7.99 per TB per month | No egress fee | Minimum charge of 1 TB and a 90-day minimum storage period. Read the usage terms before serving heavy public traffic |
| DigitalOcean Spaces | $5 per month includes 250 GiB, then $0.02 per GiB | 1 TiB included, then $0.01 per GiB | Simple pricing and a built-in CDN |
| Indonesian providers | Varies by provider | Depends on the package and provider | Several local cloud providers offer S3-compatible storage, useful when data must stay in Indonesia |
Storage is rarely the expensive part. For photos and videos, the bill is driven by how many gigabytes users download every month. That is why the same 500 GB library can cost under $10 or over $250 a month depending on how it is delivered.
Example: 500 GB of Media and 2 TB of Downloads per Month
Take a marketplace or learning platform that stores 500 GB of photos and short videos, and whose users, mostly in Indonesia, download about 2,000 GB a month. The table shows storage and delivery only. Request fees are left out because they are small at this scale: ten million image views cost around $4 on S3 and fall inside the R2 free tier.
| Setup | Storage | Delivery | Estimated total per month |
|---|---|---|---|
| Amazon S3 Jakarta, served directly | $12.50 | $250.80 | about $263 |
| Amazon S3 Jakarta + CloudFront pay-as-you-go | $12.50 | $120.00 (first 1 TB free, then $0.12 per GB) | about $133 |
| Amazon S3 Jakarta + CloudFront Pro flat-rate plan | $12.50 | $15.00 (includes 50 TB and 10 million requests) | about $28 |
| Bunny Storage + Bunny CDN standard network | $5.00 | $60.00 | about $65 |
| DigitalOcean Spaces | $9.30 (including the $5 base fee) | $8.40 | about $18 |
| Backblaze B2 | $3.48 | $5.00 | about $8.50 |
| Cloudflare R2 | $7.35 | $0 | about $7.35 |
A few things the table does not show. Serving straight from S3 without a CDN is the most expensive option and also the slowest for users far from Jakarta. CloudFront flat-rate plans have fixed allowances and feature limits, so check them against your traffic before relying on them. Bunny also offers a cheaper volume network at $0.005 per GB, but it has far fewer locations, so latency for users in Indonesia should be tested first. R2 and B2 look cheapest, yet their nearest storage location is not in Indonesia, which matters for upload speed and for data location rules.
Compare that with the server disk. A VPS with 500 GB of disk and a large bandwidth quota may cost less than any of the above on paper. The real cost shows up elsewhere: an extra server or volume for backups, the hours spent when the disk fills up, and the slower application while videos are streaming.
Photos and Videos Need More Than Storage
A 6 MB photo from a phone camera should not be sent as-is to a product listing page. Generate smaller sizes and modern formats such as WebP or AVIF, either once at upload time in a background job or on the fly through an image service. Bunny Optimizer costs $9.50 per website per month, Cloudflare and the major clouds have their own image services, and imgproxy is a popular self-hosted option.
Videos have the same issue on a larger scale. A raw MP4 uploaded from a phone is large and does not adapt to the viewer connection. Video platforms transcode it into HLS with several resolutions, so a user on a slow mobile network gets 480p instead of buffering. Bunny Stream does not charge for encoding and bills storage and delivery from $0.01 and $0.005 per GB. Cloudflare Stream bills per minute stored and delivered, and on AWS the usual path is MediaConvert with S3 and CloudFront. For an application where video is central, these services are usually cheaper than building and running your own transcoding servers.
Data Location and Regulations
Photos of faces, ID cards, and medical documents count as personal data under the Indonesian Personal Data Protection Law (UU PDP). Under PP 71/2019, private electronic system operators may generally store data outside Indonesia, but sector regulators such as OJK for finance can set stricter rules. If your data must stay in the country, use the Jakarta regions of AWS or Google Cloud, or a local provider. Whatever the location, keep buckets with personal data private and share files through short-lived signed URLs.
Moving from the Server Disk to Cloud Storage
- 1Make sure the application reads and writes files through a storage abstraction, and that the database stores relative paths or keys, not full URLs.
- 2Create the bucket, keep it private, and give the application credentials that can only access that bucket.
- 3Copy existing files with a tool such as rclone or the AWS CLI sync command, keeping the same folder structure as keys.
- 4Switch new uploads to the bucket, and for a short period let the application fall back to the local disk when a file is not found yet.
- 5Run a final sync, compare file counts and total size, then point downloads to the CDN.
- 6Keep the old folder for a few weeks as a safety net before deleting it to free the disk.
A useful rule of thumb: if your files fit comfortably on one disk and are mostly read by internal staff, the server disk with a proper offsite backup is fine. Once the files are mainly photos and videos for customers, cloud storage with a CDN almost always ends up cheaper and more reliable.
Key takeaways
- The server disk is fine for small internal applications on one server, as long as uploads are backed up somewhere else.
- For large photo and video libraries, cloud storage removes disk-full incidents, simplifies backups, and keeps file traffic away from the application server.
- Download traffic, not storage, drives the bill. Always put a CDN in front, or choose a provider with free or cheap egress.
- Process images into smaller sizes and videos into HLS, using services such as Bunny Optimizer, Bunny Stream, or Cloudflare Stream when video is central.
- Write code through a storage abstraction from day one, so moving between disk and providers is a configuration change.


