Storage path structure
All media lands under a configurable upload prefix (default:pindeck/media-uploads), organized by date:
| Variant | Location | Format |
|---|---|---|
| Original | original/ | As uploaded or generated (JPG, PNG, WebP, etc.) |
| Preview | preview/ | WebP, 640×360 crop |
| Low derivative | low/ | WebP, 320px width |
| High derivatives | high/ | WebP, 768px and 1280px width |
MKCOL before each PUT. If a directory already exists, the 405 response is treated as success.
Public delivery options
The shared-folder approach is strongly preferred. It requires only one share to be created in NextCloud and avoids per-file OCS API calls, which are slower and harder to manage at scale.
Option 1: Shared folder (preferred)
Create a single public share of your upload root folder in NextCloud, then setNEXTCLOUD_PUBLIC_SHARE_TOKEN in Convex Project Settings. Pindeck derives public image URLs using the pattern:
Option 2: Per-file shares
IfNEXTCLOUD_PUBLIC_SHARE_TOKEN is not set, Pindeck creates an individual public share for each file through the NextCloud OCS API (/ocs/v2.php/apps/files_sharing/api/v1/shares). This works but generates a large number of share records over time.
Setup
Create a NextCloud app password
In NextCloud, go to Settings → Security → Devices & sessions and generate an app password for Pindeck. Do not use your main account password.
Create a public share (recommended)
In NextCloud, share the
pindeck/media-uploads folder (or your chosen upload prefix) as a public link. Copy the share token from the link URL — it is the alphanumeric string after /s/.Set Convex environment variables
In Convex Project Settings → Environment Variables, add the following:
Image record status fields
Each image record tracks its NextCloud persistence state:| Field | Values | Description |
|---|---|---|
nextcloudPersistStatus | pending | succeeded | failed | Whether the file has been written to NextCloud |
nextcloudPersistError | string | Error message when persist failed |
derivativeUrls | { small, medium, large } | Public URLs for resized derivatives |
derivativeStoragePaths | { small, medium, large } | WebDAV paths used for cleanup on delete |
Backfilling failed uploads
If images are stuck withstorageProvider: "convex" and have not been persisted to NextCloud, run the backfill mutation to reschedule them:
sourceType = "upload", storageProvider = "convex", and storageId is still present. Run it multiple times if you have more than 50 images to backfill.
Environment variables reference
Set all of these in Convex Project Settings → Environment Variables.| Variable | Required | Description |
|---|---|---|
NEXTCLOUD_WEBDAV_BASE_URL | Yes | Full WebDAV root URL, e.g. https://cloud.example.com/remote.php/dav/files/user |
NEXTCLOUD_WEBDAV_USER | Yes | NextCloud username |
NEXTCLOUD_WEBDAV_APP_PASSWORD | Yes | NextCloud app password (not your account password) |
NEXTCLOUD_UPLOAD_PREFIX | No | Upload path prefix; defaults to pindeck/media-uploads |
NEXTCLOUD_PUBLIC_SHARE_TOKEN | Recommended | Public share token for shared-folder URL derivation |
NEXTCLOUD_UPLOAD_SHARE_TOKEN | Optional | Separate write-enabled share token for backend uploads |
NEXTCLOUD_PUBLIC_SHARE_PATH | Optional | Overrides the shared root path; defaults to NEXTCLOUD_UPLOAD_PREFIX |
NEXTCLOUD_PUBLIC_BASE_URL | Optional | Overrides the NextCloud server base URL for public links (useful when NextCloud is behind a proxy) |