Web GUI for managing Cloudflare R2 buckets and objects
488
A small web GUI for managing Cloudflare R2 buckets and the files inside them — browse, upload, download, share, rename and delete, with a password-protected login and a settings page for swapping the R2 token.
Sister app to cloudflare-dns,
azure-dns, dodns-gui
and kea-reservations — same Flask + static-JS
shape, same auth model, same deploy pipeline.
An R2 API token. In the Cloudflare dashboard: R2 → API → Manage API tokens → Create API token, and pick a permission level:
| Token permission | Browse & manage files | List buckets | Create/delete buckets |
|---|---|---|---|
| Admin Read & Write | yes | yes | yes |
| Admin Read only | read only | yes | no |
| Object Read & Write | yes | no | no |
| Object Read only | read only | no | no |
Admin Read & Write is recommended. With an object-scoped token, ListBuckets is denied by
R2, so enter the bucket names manually under Settings → R2 Credentials → Advanced → Bucket
list and the app will use those instead.
Cloudflare shows you different things depending on where you make the token, so the app accepts either:
id, and the Secret Access Key is the
SHA-256 hash of the token value. The app looks up the id via Cloudflare's token-verify endpoint
and computes the hash, so neither has to be entered.The Account ID is auto-detected when the token is allowed to read the account list; tokens scoped to R2 only usually are not, so it has to be entered once. It is not a secret — it is on the R2 overview page and in the dashboard URL.
docker run -d --name r2-gui --restart unless-stopped \
-p 127.0.0.1:5000:5000 \
-v /opt/r2-gui/data:/app/data \
--label com.centurylinklabs.watchtower.enable=true \
awkto/r2-gui:latest
Then open the app, set an admin password on first load, and add the R2 credentials under
Settings. Everything is stored in /app/data/.env, so mount that directory to keep the
configuration across restarts and image updates.
Or with compose:
docker compose up -d
Uploads go through the app, so raise the body-size limit. For nginx:
client_max_body_size 5g;
proxy_request_buffering off;
proxy_read_timeout 1800s;
proxy_send_timeout 1800s;
Credentials are normally set in the UI, but can be pre-seeded with environment variables
(see .env.example):
| Variable | Purpose |
|---|---|
R2_ACCOUNT_ID | Cloudflare account ID (builds the S3 endpoint) |
R2_ACCESS_KEY_ID | R2 access key ID |
R2_SECRET_ACCESS_KEY | R2 secret access key |
R2_API_TOKEN | Cloudflare API token, when the two values above were derived from it |
R2_ENDPOINT | Optional endpoint override for jurisdiction-restricted buckets |
R2_BUCKETS | Optional comma-separated bucket names for object-scoped tokens |
SESSION_SECRET, API_TOKEN and ADMIN_PASSWORD_HASH are generated on first run and written
to /app/data/.env — do not set them by hand.
Every /api/* route accepts either the browser session cookie or an
Authorization: Bearer <token> header, where the token comes from Settings → API Token.
Interactive docs live at /apidocs/.
GET /api/health
GET /api/buckets
POST /api/buckets {name, location?}
DELETE /api/buckets/<bucket>
GET /api/buckets/<bucket>/objects?prefix=
POST /api/buckets/<bucket>/objects multipart: prefix, files[]
DELETE /api/buckets/<bucket>/objects {keys[], prefixes[]}
POST /api/buckets/<bucket>/folders {prefix, name}
POST /api/buckets/<bucket>/move {source, destination, is_folder}
GET /api/buckets/<bucket>/download?key=
POST /api/buckets/<bucket>/presign {key, expires}
GET /api/buckets/<bucket>/preview?key=
GET /api/config POST /api/config POST /api/config/test
Example:
curl -H "Authorization: Bearer $R2GUI_TOKEN" https://r2.example.com/api/buckets
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
.venv/bin/python app.py # http://localhost:5000
Tag with semver; the GitHub Action builds and pushes awkto/r2-gui:<version> and :latest to
Docker Hub, and watchtower picks up the new :latest on the deployed host within ~5 minutes.
git tag v1.0.1 && git push origin main v1.0.1
Content type
Image
Digest
sha256:501406814…
Size
72.6 MB
Last updated
about 2 months ago
docker pull awkto/r2-gui