DigitalOcean Spaces is object storage that speaks the S3 API, with a content delivery network attached at no extra charge. It is where you put files that your application creates or serves but should not be storing on its own disk — user uploads, images, video, backups, and static assets.
Object storage is one of those pieces of infrastructure that seems unnecessary until the day it becomes obviously necessary, usually the day after a redeploy quietly deleted a month of user uploads. This is a practical explanation of what Spaces is, what object storage is genuinely for, and the three decisions that actually matter when you adopt it — which are the same three decisions whichever S3-compatible provider you end up using.
Table of contents
- What object storage is, and what it is not
- What Spaces specifically gives you
- How the pricing is shaped
- The three decisions that actually matter
- Wiring it into an application
- Where this sits next to the rest of your stack
- How this fits the rest of the stack
- FAQ
What object storage is, and what it is not
Object storage keeps files as objects in a flat namespace, addressed by a key, and reached over HTTP. There is no filesystem underneath it. There are no directories, only keys that contain slashes and a console that renders them as if they were folders. You cannot mount it and open a file halfway through and write four bytes.
That constraint is the whole design. Because there is no filesystem to keep consistent, the store can be replicated widely, scaled without a resize operation, and served directly to browsers. What you give up is in-place modification: updating an object means writing the whole object again.
The two things it is regularly confused with:
- Block storage — a volume attached to one machine that behaves like a disk. Right for a database’s data directory. Wrong for files you want to serve to the public.
- A filesystem on the app server — the default that everyone starts with. Fine until you have two instances, or one redeploy.
The rule of thumb that holds up: if a file needs to survive the application that created it, and more than one process may want to read it, it belongs in object storage.
What Spaces specifically gives you
Spaces is DigitalOcean’s implementation. The characteristics that matter when evaluating it:
- S3 API compatibility. The reason this matters is not brand-agnosticism in the abstract — it is that every SDK, CLI, backup tool, WordPress plugin and framework storage backend already speaks S3. Your library selection problem disappears.
- A built-in CDN, included rather than priced separately, caching objects closer to users.
- Buckets — one to start with, and up to 100 on an account.
- A published request-rate ceiling of 1,500 requests per second per client IP address.
- A cold storage tier for infrequently accessed data, with feature and performance limitations relative to standard storage.
The request-rate ceiling is the specification most worth noting, because it is per client IP rather than per bucket. For browser traffic that is effectively unlimited, since each visitor is a different address. For a server-side job hammering the API from one machine — a bulk migration, a mass thumbnail generation run — it is a real ceiling you can hit, and the symptom is throttling rather than a clean error.
How the pricing is shaped
Rather than quote figures that will be stale by the time you read this, here is the shape, which changes far less often than the numbers:
- A flat monthly base that includes an allowance of storage and an allowance of outbound transfer. This is the part that makes small projects predictable — under the allowance, the bill does not move.
- Per-GiB overage on stored data beyond the included allowance.
- Per-GiB overage on outbound transfer beyond the included allowance. Inbound is free, which is standard across object storage and worth knowing when planning a migration in.
- A cheaper per-GiB rate for cold storage, in exchange for retrieval and feature constraints.
Check the current numbers on the vendor’s pricing page before budgeting; they move, and a blog post is not a billing source.
The structural insight that outlives any particular price list: outbound transfer, not storage, is what makes object storage bills surprising. Storing a hundred gigabytes is cheap and predictable. Serving that hundred gigabytes to a lot of people repeatedly is the variable. If your usage is archival, you will find object storage almost free. If you are serving video to a growing audience, model the egress carefully, because that line is the one that grows with your success.
The three decisions that actually matter
Whichever provider you pick, adoption comes down to the same three choices, and getting them wrong is more expensive than picking the wrong vendor.
Public or private objects. Public objects are served directly by URL, which is what you want for site assets. Private objects require a signed URL with an expiry, which is what you want for anything a user uploaded and only they should see. Setting a bucket public because signed URLs looked like effort is how private documents end up indexed by a search engine. Decide per bucket, not per file.
Key naming. There is no rename operation. Changing an object’s key means copying it and deleting the original, which on a large bucket is a job rather than an action. Design the key structure before uploading anything — a version or tenant prefix costs nothing on day one and is close to impossible to retrofit.
Cache headers. Objects are served with the cache headers you set at upload time. Set them once, correctly: long max-age with a content hash in the filename for immutable assets, short or no-cache for anything mutable. The CDN will honour whatever you told it, including a mistake, for as long as you told it to.
Wiring it into an application
Because it is S3-compatible, the integration is whatever your language’s S3 library already does, pointed at a different endpoint. In Python:
import boto3
session = boto3.session.Session()
client = session.client(
's3',
region_name='nyc3',
endpoint_url='https://nyc3.digitaloceanspaces.com',
aws_access_key_id=os.environ['SPACES_KEY'],
aws_secret_access_key=os.environ['SPACES_SECRET'],
)
client.upload_file(
'local.jpg', 'my-bucket', 'uploads/2026/local.jpg',
ExtraArgs={'ACL': 'private', 'CacheControl': 'max-age=31536000'},
)
The only line that differs from talking to S3 itself is endpoint_url. That is the practical meaning of S3-compatible, and it is also the reason this is a low-lock-in decision: moving to a different S3-compatible provider later is a configuration change plus a data copy, not a rewrite.
For frameworks, the same applies one level up. Django’s storage backends, Laravel’s filesystem drivers, and the common WordPress offload plugins all take an S3 endpoint as configuration. You are choosing a URL and a pair of credentials, not a library.
Where this sits next to the rest of your stack
Object storage is deliberately a separate concern from where your application runs, and it is normal and sensible for them to live with different providers. The store is reached over HTTPS with credentials; it does not care what is calling it.
The practical arrangement for most applications: the app runs as a service somewhere, the database is managed, and files go to an object store. On RunxBuild the first two are a service deployed from your GitHub repository with build logs, a live route, environment variables and rollback, plus a managed Postgres or MySQL beside it with backups and private networking. Persistent storage attaches to a service for the cases where a real filesystem is genuinely the right answer, and a static site includes 120GB of bandwidth before $0.10/GB.
The decision worth thinking about is not which vendor. It is which of your files belong on an attached volume and which belong in an object store — and the dividing line is whether the file needs to be served to the public and outlive the instance that wrote it.
Get that split right and swapping providers later is a weekend. Get it wrong and you will be moving data out of a container filesystem you have been treating as durable, which is a considerably worse weekend.
How this fits the rest of the stack
Spaces is a competent S3-compatible object store with a bundled CDN, and the useful things to know about it are mostly things that are true of object storage in general: no filesystem, no rename, egress rather than storage drives the bill, and the public-versus-private decision is the one with security consequences. Design the key structure and the cache headers before you upload, because both are painful to change afterwards. If you are working out what the whole stack costs — the service, the database, the attached storage, the bandwidth — the RunxBuild hosting calculator puts those on one page as separate line items, which makes the storage decision easier to reason about in context.
Useful related references:
- Docker Space: Docker Hub, Hugging Face Spaces, and Container Registries
- RunxBuild vs DigitalOcean: Cloud Hosting Compared
- DigitalOcean vs AWS: Where the Price Gap Actually Comes From
- Storage on RunxBuild
FAQ
What is DigitalOcean Spaces used for?
Storing and serving files that should not live on an application server’s disk: user uploads, images and video, static site assets, database backups, and archives. It is S3-compatible object storage with a built-in CDN, so anything that already works with the S3 API works with it.
Is Spaces the same as S3?
Not the same service, but the same API. Spaces implements the S3 interface, so existing S3 SDKs, CLI tools and framework storage backends work by changing the endpoint URL. That compatibility is what keeps the switching cost low in either direction.
What is the difference between Spaces and block storage?
Block storage attaches to one machine and behaves like a disk — right for a database data directory. Object storage is reached over HTTP, has no filesystem, and can be served directly to browsers — right for uploads and public assets. They solve different problems and most applications need both.
Does object storage pricing depend on storage or bandwidth?
Both, but bandwidth is what makes bills surprising. A flat monthly base includes an allowance of each, with per-GiB overage beyond it. Stored data is cheap and predictable; outbound transfer scales with how often your files are actually requested, which is the line that grows as your traffic does.
Can I rename a file in object storage?
No. There is no rename operation. You copy the object to the new key and delete the original, which is fine for one file and a batch job for a million. This is why the key naming scheme is worth designing before the first upload rather than after.