Persistent storage & object storage
Keep the files your app writes. Pick the paths that survive a redeploy, or connect an S3 bucket so uploads live outside the container entirely.
Every deploy replaces your app's container with a fresh copy. That's what makes deploys safe to repeat, and it's also why a file your app wrote yesterday is gone today unless you tell Synq to keep it. This page covers two ways to do that: persistent storage for paths on the server, and object storage for a bucket that lives outside your app altogether.
Which one do I want?
- Uploads that users must never lose: object storage (a bucket). It also keeps working when you run more than one copy of the app.
- A cache, a SQLite file, a framework's own storage folder: persistent storage (a path).
- A named volume you want to see, size, back up and restore on its own: the Volumes manager.
- A real database: neither. Add a database service and connect it (see Internal networking).
Where it is
Open the project and choose Storage in the project menu. For a single app it shows, from top to bottom: Volumes, Persistent storage, File mounts and Object storage. The same cards (except Volumes) are also on the Configuration tab.
For a compose or monorepo project, volumes are set per service. The Storage section shows the Volumes manager and a Volumes are set per service card whose button opens Apps & Services; each service has its own Volumes tab.
Persistent storage (paths that survive a deploy)
Synq already knows what some frameworks write. A Laravel app keeps storage/ with no configuration at all,
because that's where Laravel puts uploads, sessions, cache and its logs.
Look at the Persistent storage card
It lists the paths currently kept and says where the list came from: Framework defaults for this stack. means Synq chose it, Set for this project. means you did, and Nothing is kept between deploys. means the list is empty.
Press Edit and list the paths, one per line
A plain path is relative to your app:
storage
public/uploadsYou can also give a full mount to choose the volume name, or to use a folder on the server:
uploads:/app/public/uploads
/srv/app-data:/app/varSave, then deploy
Press Save. The message says Storage updated. It applies on the next deploy. The running container keeps the mounts it started with.
To go back to the framework's own list, press Use framework defaults. To keep nothing at all, save an empty list: that's a real choice, and Synq won't quietly add the defaults back.
You can declare the same thing in your repository, so a fresh deploy of the same code gets it automatically:
{
"volumes": ["storage", "uploads:/app/public/uploads"]
}Things worth knowing
- The first deploy seeds the folder. Whatever your app ships at that path (Laravel's
storage/skeleton, for instance) is copied in, so the app doesn't start against an empty folder. - Later releases don't refresh it. That's the point: it's your data now, not the image's. So don't list a
path that also contains code. Laravel's
database/folder holds migrations as well as the SQLite file, which is why Synq keepsstorage/and notdatabase/. - Volume names are scoped per project. Two projects that both keep
storagenever share one volume. The named volumes these paths create also appear in the Volumes manager, where you can back them up. A folder on the server (/srv/app-data:/app/var) is a plain bind mount and isn't listed there. - One copy per server. A path lives on one server's disk. Two copies of the app on different servers each see their own folder; use object storage for shared uploads.
Object storage (an S3 bucket)
A path on the server stops data loss, but it doesn't scale: run two copies of your app and each one gets its own uploads folder, which shows up as files that exist for some visitors and 404 for others. A bucket is shared by every copy and outlives the server.
Synq connects a bucket using your framework's own variable names, so you don't have to hunt for them. A
Laravel app gets FILESYSTEM_DISK=s3 plus the AWS_* variables its S3 disk reads; a Django app gets the
AWS_STORAGE_BUCKET_NAME / AWS_S3_* names django-storages reads; anything else gets neutral S3_* names.
Press Connect on the Object storage card
The card says No bucket connected. Uploads stay on the container's disk. until you do.
Choose the source
- An installed MinIO app: if you installed MinIO from the Apps catalog, pick it under Source. Its endpoint and keys are filled in, your app joins its private network so the traffic never leaves the server, and the bucket MinIO created on install is prefilled.
- External provider: pick the Provider (Amazon S3, Cloudflare R2, Wasabi, Backblaze B2, DigitalOcean Spaces or Other S3-compatible) and fill in Bucket, Endpoint (not needed for Amazon S3), Region, Access key ID and Secret access key.
Press Test & connect
Synq writes, reads and deletes one throwaway object before saving anything. If the bucket doesn't exist or the keys are read-only, you find out here, not the first time a user uploads a file. Then: Bucket verified and connected. It applies on the next deploy.
The card then shows the bucket, the provider and exactly which Variables were set. Disconnect removes those variables again: only the ones the connection added, never anything you set by hand.
Where the credentials live
The keys are stored as encrypted, masked project variables, like all your secrets (see Environment variables). There's no second copy anywhere, and disconnecting deletes them.
Using the bucket from your code
Once connected, your framework's normal file API writes to the bucket: Storage::put(...) in Laravel,
default_storage in Django. For other stacks, read the S3_* variables the card lists and hand them to your
S3 client.
Related guides
Cloudflare Tunnel and external proxies
Let Synq run a Cloudflare Tunnel on your server and route domains through it, or put your own tunnel, Cloudflare's proxy, Nginx Proxy Manager or a load balancer in front with External ingress & TLS.
File mounts
Put a config file, certificate or key into a container without baking it into the image. Synq stores the contents encrypted, writes the file on the server and mounts it read-only.