Welcome! Type "help" for available commands.
$
Loading terminal interface...
Back to Blog

How I Finally Got a DigitalOcean Spaces Bucket to Default Public (Spoiler: Ditch Per-Bucket Keys)

July 20, 2025
William Callahan

Software engineer and founder with a background in finance and tech. Currently building aVenture.vc, a platform for researching private companies. Based in San Francisco.

digitaloceans3object storageaws-clis3cmddevopssysadminhostingcdnbucket policyacl
How I Finally Got a DigitalOcean Spaces Bucket to Default Public (Spoiler: Ditch Per-Bucket Keys)

What This Post Covers

This guide covers:

  • Why Per-Bucket Access Keys are the secret villain behind most 403 Forbidden policy errors.
  • The dead-simple ACL approach that works every time.
  • When you can safely use a JSON bucket policy (and when to save yourself the headache).
  • How to turn on server access logging—no support ticket required anymore.

I thought making a bucket public on DigitalOcean Spaces would be a ten-second checkbox. Instead I burned an afternoon chasing 403 Forbidden errors and second-guessing my JSON.

The truth I wish I'd known: if you leave Per-Bucket Access Keys enabled you can't modify most bucket policies—period. Turn that feature off, switch to a regular all-or-nothing access key, and everything snaps into place.

Below is the exact sequence that finally worked for me, with the false turns called out so you don't repeat them.

1. Disable Per-Bucket Access Keys (Yes, Really)

DigitalOcean's per-bucket keys sound great on paper: tight scoping, least-privilege, all that good stuff. Unfortunately the current implementation blocks PutBucketPolicy, PutBucketLogging, and a few other S3 calls—even if the UI insists the key has those permissions.

Fix: In the Spaces dashboard, open your bucket ➜ Settings ➜ toggle Per-Bucket Access Keys off. Grab a classic Spaces key (or create a new one) that has full account-wide access. Use that for every command below.

If you're still seeing 403s after this step, double-check that your CLI is using the new key.

2. Install s3cmd (AWS CLI Works, But s3cmd is Friendlier Here)

# macOS
brew install s3cmd

# Ubuntu / Debian
sudo apt-get install s3cmd

Run s3cmd --configure and drop in your Spaces key, secret, and region (for me: sfo3). When it asks for the endpoint, use sfo3.digitaloceanspaces.com (swap for your region).

3. The No-Drama ACL Method

This is the zero-friction route I recommend.

a. Make the bucket listable

s3cmd setacl s3://YOUR_BUCKET --acl-public

b. Flip every existing object to public

s3cmd setacl s3://YOUR_BUCKET/ --acl-public --recursive

c. Upload new files as public by default

s3cmd put local-file.jpg s3://YOUR_BUCKET/remote-file.jpg --acl-public

That's it. Skip the rest of the post unless you love yak-shaving.

4. The JSON Bucket Policy Rabbit Hole (Optional)

Feel like you must use a proper policy? Fine—just remember the Per-Bucket Keys warning above.

  1. Create public-policy.json:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::YOUR_BUCKET/*"
    }
  ]
}
  1. Push it with the AWS CLI (endpoint is mandatory):
aws s3api put-bucket-policy \
  --bucket YOUR_BUCKET \
  --endpoint-url https://YOUR_REGION.digitaloceanspaces.com \
  --policy file://public-policy.json

If that still throws a 403, triple-check that you're using a non-scoped key.

5. Good News: Access Logging is Self-Serve Now

Back in early 2025 you had to open a support ticket to enable server access logging. As of July 2025 the feature is rolled out for everyone—no ticket required.

Enable it with a single call:

aws s3api put-bucket-logging \
  --bucket YOUR_SOURCE_BUCKET \
  --endpoint-url https://YOUR_REGION.digitaloceanspaces.com \
  --bucket-logging-status '{
    "LoggingEnabled": {
      "TargetBucket": "your-log-destination-bucket",
      "TargetPrefix": "logs/"
    }
  }'

If you get a 403 here, you already know the likely cause: you forgot to disable Per-Bucket Access Keys.


tl;dr

  1. Turn off Per-Bucket Access Keys.
  2. Use ACLs for the fastest public-by-default setup.
  3. Policies and access logging work once you're using a full-access key—no more support tickets.

Similar Content

Home
CV
ExperienceEducation
ProjectsBookmarksInvestmentsContactBlog
Welcome! Type "help" for available commands.
$
Loading terminal interface...

Similar Content

Related Articles

September 25, 2025
How to Secure Environment Variables for LLMs, MCPs, and AI Tools Using 1Password or Doppler

How to Secure Environment Variables for LLMs, MCPs, and AI Tools Using 1Password or Doppler

Stop hardcoding API keys in MCP configs and AI tool settings. Learn how to use 1Password CLI or Doppler to inject secrets just-in-time for Claude, Cur...

security1passworddopplermcpaillm+10
BLOG
August 22, 2025
Claude Code Output Styles: Explanatory, Learning, and Custom Options

Claude Code Output Styles: Explanatory, Learning, and Custom Options

An implementation guide to Claude Code's /output-style, the built‑in Explanatory and Learning modes (with to-do prompts), and creating reusable custom...

aiclaude codeoutput styleslearningcustom stylesexplanatory+7
BLOG
February 10, 2025
How I Host My Apps: How to Deploy in 2025

How I Host My Apps: How to Deploy in 2025

The hosting setup I use for aVenture.vc, plus why you shouldn't waste time with complex cloud services when starting out.

devopsdockercloudstartupshostapps+6
BLOG

Related Bookmarks

ampcode.com
January 13, 2026
How to lazy load MCPs to save context capacity with agent skills

How to lazy load MCPs to save context capacity with agent skills

Load MCP tools into your context window only when you use them

claude codeclaude code enhancementsclaude code usageclaude integrationsclaude mcp integrationsamp+15
LINK
github.com
August 8, 2025
GitHub - gotify/server: A simple server for sending and receiving messages in real-time per WebSocket. (Includes a sleek web-ui)

GitHub - gotify/server: A simple server for sending and receiving messages in real-time per WebSocket. (Includes a sleek web-ui)

A simple server for sending and receiving messages in real-time per WebSocket. (Includes a sleek web-ui) - gotify/server

open source projectsself-hosted messaging serversreal-time communicationwebsocket applicationsrest apisserver+7
LINK

Related Projects

repo-tokens-calculator

repo-tokens-calculator

CLI token counter (Python + tiktoken + uv) with pretty summary

clipythontiktokenuvdeveloper toolsopen source+8
PRJ
williamcallahan.com

williamcallahan.com

Interactive personal site with beautiful terminal/code components & other dynamic content

graph indexs3 object storageinteractive appterminal uimdx blogsearch+8
PRJ
ComposerAI

ComposerAI

AI email client / mailbox for agentic search and tasks

aiemail clientllmproductivitytask automationvector search+10
PRJ

Related Books

The Complete Obsolete Guide to Generative AI

The Complete Obsolete Guide to Generative AI

David Clinton

david clintoncompleteobsoleteguidegenerative
BOOK

Related Investments

Vest

Vest

Vest is an application that allows investing in the US stock market in Latin America.

investment platformsseedactivevestmarketapplication+5
INV
Switch

Switch

Switch provides a simple way to pool money and spend with a group.

paymentspre-seedactiveswitchprovidessimple+5
INV
Anfin

Anfin

Vietnamese investment platform making stock and crypto investing accessible to retail investors.

financeseedactiveanfininvestmentplatform+5
INV