Guides

External object storage

Need somewhere to keep uploads, images, or files? Astrodock can hand your app a bucket on the bundled storage automatically, or you can point it at an S3-compatible service you already use — AWS S3, Cloudflare R2, DigitalOcean Spaces, and friends. As with the database, your app code is the same either way.

Internal or external?

Each app picks a storage mode in app.json:

ModeWhat Astrodock doesReach for it when…
internalAuto-provisions a bucket on the bundled object store and injects all the connection details, plus a per-app key prefix.You just need storage and don't care where the bytes live.
externalNothing is provisioned. You supply five credentials as secrets; the deploy is blocked until you do.You already use S3/R2/Spaces, or want a CDN, lifecycle rules, or storage outside the box.
noneNo storage, nothing injected.The app doesn't store files.

Step 1 — Set the mode in app.json

{
  "schemaVersion": "1",
  "slug": "gallery",
  "name": "Gallery",
  "subdomain": "gallery",
  "runtime": { "type": "node" },
  "auth": { "mode": "platform" },
  "database": { "mode": "none" },
  "storage": { "mode": "external" }
}

Apply it:

astrodock apply

Step 2 — Set the five storage secrets

With storage external, these five ASTRODOCK_STORAGE_* values become required secrets. The deploy is blocked until all five exist — the required-variable gate will list any that are missing:

SecretWhat it is
ASTRODOCK_STORAGE_ENDPOINTThe S3 API endpoint URL.
ASTRODOCK_STORAGE_REGIONThe region (some providers want a placeholder like auto or us-east-1).
ASTRODOCK_STORAGE_BUCKETThe bucket name.
ASTRODOCK_STORAGE_ACCESS_KEYThe access key ID.
ASTRODOCK_STORAGE_SECRET_KEYThe secret access key.

Set each one with set-secret (pipe the secret ones via stdin to keep them out of your shell history):

astrodock set-secret ASTRODOCK_STORAGE_ENDPOINT 'https://s3.us-east-1.amazonaws.com'
astrodock set-secret ASTRODOCK_STORAGE_REGION   'us-east-1'
astrodock set-secret ASTRODOCK_STORAGE_BUCKET   'my-app-uploads'
printf '%s' 'AKIA…'        | astrodock set-secret ASTRODOCK_STORAGE_ACCESS_KEY
printf '%s' 'wJalrXUtn…'   | astrodock set-secret ASTRODOCK_STORAGE_SECRET_KEY

Prefer the dashboard?

Open the app and use the Env tab to set the same five keys. See Secrets & environment variables for details.

Provider examples

Same five keys, different values. Here's the shape for the common providers:

AWS S3

ASTRODOCK_STORAGE_ENDPOINThttps://s3.us-east-1.amazonaws.com
ASTRODOCK_STORAGE_REGIONus-east-1
ASTRODOCK_STORAGE_BUCKETmy-app-uploads
ASTRODOCK_STORAGE_ACCESS_KEY / ..._SECRET_KEYAn IAM access key pair.

Cloudflare R2

ASTRODOCK_STORAGE_ENDPOINThttps://<account-id>.r2.cloudflarestorage.com
ASTRODOCK_STORAGE_REGIONauto
ASTRODOCK_STORAGE_BUCKETYour R2 bucket name.
ASTRODOCK_STORAGE_ACCESS_KEY / ..._SECRET_KEYAn R2 API token's key pair.

DigitalOcean Spaces

ASTRODOCK_STORAGE_ENDPOINThttps://nyc3.digitaloceanspaces.com
ASTRODOCK_STORAGE_REGIONnyc3
ASTRODOCK_STORAGE_BUCKETYour Space name.
ASTRODOCK_STORAGE_ACCESS_KEY / ..._SECRET_KEYA Spaces access key pair.

Your app uses the same S3 SDK either way

Internal or external, your app reads the same variable names and uses the same S3 client — only the values differ:

import { S3Client } from '@aws-sdk/client-s3';
const s3 = new S3Client({
  endpoint: process.env.ASTRODOCK_STORAGE_ENDPOINT,
  region: process.env.ASTRODOCK_STORAGE_REGION,
  credentials: {
    accessKeyId: process.env.ASTRODOCK_STORAGE_ACCESS_KEY,
    secretAccessKey: process.env.ASTRODOCK_STORAGE_SECRET_KEY
  },
  forcePathStyle: true
});

A note on the prefix

When storage is internal, Astrodock also injects ASTRODOCK_STORAGE_PREFIX — a per-app key prefix so multiple apps can safely share the bundled bucket. Prefix your object keys with it when it's present:

const prefix = process.env.ASTRODOCK_STORAGE_PREFIX || '';
const key = prefix + 'uploads/photo.jpg';

External mode has no prefix

With external storage you own the whole bucket, so ASTRODOCK_STORAGE_PREFIX is not set. The snippet above handles both cases: it falls back to an empty string when the prefix is absent.

Next steps