Media Services
Configure where Deckyard stores uploaded images, and connect an ImageKit photo archive.
Overview
Section titled “Overview”Deckyard stores uploaded images in one of two places:
- Local disk (the default): files in
server/uploads/on the server - S3-compatible object storage: AWS S3, Scaleway Object Storage, MinIO, Wasabi, Backblaze B2 or any other S3-compatible store
Separately, you can connect ImageKit as a source to pick images from. ImageKit is not a storage option: an image picked from it is copied into your own storage, local or S3.
Choosing the Storage Mode
Section titled “Choosing the Storage Mode”# `auto` (default), `s3` or `local`MEDIA_STORAGE_MODE=autoautouses S3 whenS3_ACCESS_KEY,S3_SECRET_KEY,S3_BUCKETandS3_ENDPOINTare all set, and local disk otherwise. A partial S3 setup prints a warning at startup and falls back to local.s3always uses S3; the server refuses to start if the four variables are not all set.localalways uses local disk.
Local Storage
Section titled “Local Storage”No configuration is needed. Files are stored in server/uploads/ (change with UPLOADS_DIR) and served at /uploads/<name>-<id>.<ext>.
- Maximum upload size: 10 MB
- PNG, JPG and WebP images larger than 3840 × 2160 are scaled down to fit and recompressed in the same format
Local storage suits development and single-server installs; back up the uploads folder together with the database.
S3-Compatible Storage
Section titled “S3-Compatible Storage”S3_ACCESS_KEY=your-access-keyS3_SECRET_KEY=your-secret-keyS3_BUCKET=deckyard-mediaS3_ENDPOINT=https://s3.nl-ams.scw.cloud# Default: nl-amsS3_REGION=nl-ams# OptionalS3_PUBLIC_URL=https://media.example.comS3_ENDPOINT has no default. Examples: https://s3.eu-central-1.amazonaws.com for AWS, https://s3.nl-ams.scw.cloud for Scaleway, https://minio.example.com for MinIO.
How Uploads Work
Section titled “How Uploads Work”The browser uploads directly to the bucket: Deckyard hands out a presigned upload URL (valid for one hour), the browser sends the file, and the server records it. Files are stored as uploads/<year>/<month>/<name>-<id>.<ext>.
- Maximum upload size: 20 MB
- Images are stored as uploaded; there is no resizing or format conversion
Social preview images (og:image) are stored through the same storage mode.
Public Access
Section titled “Public Access”Without S3_PUBLIC_URL, image URLs point at the bucket itself (https://<bucket>.<endpoint-host>/...), so the bucket must be publicly readable. Set S3_PUBLIC_URL to serve images from a CDN or custom domain in front of the bucket instead.
Deckyard does not use signed URLs for reading, so a private bucket only works behind such a public URL.
Because the browser uploads straight to the bucket, the bucket needs a CORS rule that allows PUT from your Deckyard origin:
[ { "AllowedOrigins": ["https://slides.example.com"], "AllowedMethods": ["GET", "PUT"], "AllowedHeaders": ["*"], "MaxAgeSeconds": 3000 }]Legacy Scaleway Variables
Section titled “Legacy Scaleway Variables”Older installs used SCW_ACCESS_KEY, SCW_SECRET_KEY, SCW_BUCKET, SCW_REGION, SCW_ENDPOINT and SCW_CDN_URL, and MEDIA_STORAGE_MODE=scaleway. They are still read when the matching S3_* variable is unset, with a warning at startup, and will be removed in the first release after 1 November 2026. Rename them to their S3_* counterparts (SCW_CDN_URL becomes S3_PUBLIC_URL).
Allowed File Types
Section titled “Allowed File Types”Both storage modes accept PNG, JPG, GIF, WebP and SVG.
Images imported from Unsplash and Giphy are always stored on local disk.
ImageKit
Section titled “ImageKit”ImageKit connects your organization’s photo archive to the image picker.
Configuration
Section titled “Configuration”IMAGEKIT_PUBLIC_KEY=public_...IMAGEKIT_PRIVATE_KEY=private_...IMAGEKIT_URL_ENDPOINT=https://ik.imagekit.io/your-idAll three are required. Optional:
# Folder for files Deckyard uploads to ImageKitIMAGEKIT_UPLOAD_FOLDER=/deckyard# Prefix for per-deck tags (default: deck:)IMAGEKIT_TAG_PREFIX=deck:# Custom metadata field for ALT text; unset = offIMAGEKIT_METADATA_FIELD_ALT_SEED=alt_seed# Files with these tags stay out of the picker (v1.39.0+)IMAGEKIT_HIDDEN_TAGS=_duplicate,_unsortedYou find the keys and URL endpoint in your ImageKit dashboard.
Files carrying a tag from IMAGEKIT_HIDDEN_TAGS do not appear in the picker, its search results or the tag suggestions. Slides that already use them keep working.
Picking an Image
Section titled “Picking an Image”When ImageKit is configured, the image picker opens with Choose image source, listing ImageKit (“Your organisation’s photo archive”) next to Image library and Gradients. In the ImageKit picker you can:
- Search by name or tags, or use Advanced for a raw search query
- Choose a Transform: the original, or one of the named transforms
deck_slide_full_2xanddeck_thumb_2x(define these in ImageKit) - Edit tags and ALT text, generate ALT text with AI, and write it back to ImageKit
- Click Use this image
Deckyard then copies the image (up to 20 MB) into your own storage, and the slide uses that copy. Later changes in ImageKit do not reach the slide.
Copying requires uploads to be enabled. If they are off, the picker says: “This image cannot be used because copying it into your own media requires image uploads to be enabled.”
Imports That Upload to ImageKit
Section titled “Imports That Upload to ImageKit”When ImageKit is configured, three importers store their images in ImageKit instead of your own storage:
- PowerPoint slides imported as images (folder
/converted-slides) - PDF pages imported as slide images
- Images from a Notion import
IMAGEKIT_ONLY
Section titled “IMAGEKIT_ONLY”IMAGEKIT_ONLY=true turns off direct uploads and hides the image library. Because picks from ImageKit are copied into your own storage, which needs uploads, it also blocks new ImageKit picks. Existing images are not affected.
Switching Storage
Section titled “Switching Storage”Deckyard has no migration tool. Existing slides keep the URLs they were saved with, so when you move from local to S3 (or between buckets), copy the files and keep the old URLs reachable, or leave the old storage in place for existing images.
Troubleshooting
Section titled “Troubleshooting”Upload Failures
Section titled “Upload Failures”- Check the startup log for a warning about incomplete S3 configuration
- With S3: check the credentials, the bucket name and the CORS rule for
PUT - Check the file size (10 MB local, 20 MB S3) and type
Missing Images
Section titled “Missing Images”- With S3 and no
S3_PUBLIC_URL: check that the bucket is publicly readable - Check that the URL in the slide still resolves; moving storage does not rewrite existing URLs
ImageKit Not Offered or Picks Fail
Section titled “ImageKit Not Offered or Picks Fail”- ImageKit appears in the picker only when all three
IMAGEKIT_*keys are set - Picks need uploads to be enabled; check
IMAGEKIT_ONLYandUPLOADS_ENABLED