At a glance
This guide adds to the application from Deploy a Django application:Environment variables
With Ingot, the build needs every variable, because
collectstatic loads the settings, which read them without a default. The deploy guide lists the other variables.
Before you begin
- A Django project deployed on Sevalla, as in Deploy Django.
Create a bucket
1
Create the bucket
In the sidebar, click Object storage, then click Create object storage. Enter
storefront-media as the Name, choose the Location closest to the application, and click Create object storage.2
Decide who can read the files
Keep the bucket private to serve each file through a link that expires. To serve files to anyone with a permanent URL, click the bucket’s Settings tab, click Enable public access under Public access, then click Enable public access in the dialog.
Add the bucket details to the application
The values come from the bucket’s Settings tab. See Connect with the S3 API for each one.

1
Open the environment variables
In the sidebar, click Applications, then click the application. Click the Environment variables tab.
2
Add the variables
Click Add environment variables. Add the variables, keep Available during runtime and Available during buildtime selected, and click Add environment variables.
collectstatic during the build, which loads the settings, and the settings read the variables without a default. They apply with the next deployment, so add them before you deploy the new settings.
If the application builds from your own Dockerfile, its build doesn’t read them: Sevalla passes build-time variables to a Dockerfile as build arguments, which the Dockerfile doesn’t declare, so the bucket’s keys stay out of the image. Without the variables, the build stops with ImproperlyConfigured: Set the S3_BUCKET environment variable. Give collectstatic placeholders for them, next to the placeholder SECRET_KEY. collectstatic loads the settings but doesn’t touch the bucket:
Dockerfile
Install django-storages
Install django-storages 1.14 with its S3 extra, which adds boto3, and updaterequirements.txt:
Configure the storage
Instorefront/settings.py, point the default storage at the bucket. Keep the staticfiles storage from the deploy guide:
storefront/settings.py
FileField and ImageField now saves to the bucket, and file.url returns a link that works for 1 hour (django-storages’ default querystring_expire is 3600 seconds). See presigned URLs.
For a public bucket, return permanent URLs on the bucket’s public domain instead. Add the S3_PUBLIC_DOMAIN variable to the application, then add two options:
storefront/settings.py
requirements.txt included. If Auto-deploy is off, click Deploy now on the application’s Overview tab. The build log warns Django's collectstatic runs during the build, and the settings read environment variables without a default: with the variables Available during buildtime, the build passes.
Verify
When the deployment’s status is Deployed, click the application’s Web terminal tab, and in the shell that opens in the Web process, save a test file through Django’s storage:Hello from Sevalla. In the sidebar, click Object storage, click the bucket, and the Objects tab lists hello.txt. To remove it, run default_storage.delete('hello.txt') the same way, or delete it on the Objects tab. See Manage objects.
A script or an AI agent runs the same command with Run a command in a process (execProcessCommandV3), opens the URL it prints, and lists the bucket with List objects (listObjectStorageObjectsV3). APP_ID and WEB_ID come from Deploy with the API, and BUCKET_ID from Deploy with the API:
Serve static files from the bucket
WhiteNoise with the Sevalla CDN serves static files well for most projects. To serve them from the bucket instead, make the bucket public and give media and static files their own folders withlocation:
storefront/settings.py
whitenoise.middleware.WhiteNoiseMiddleware from MIDDLEWARE. The collectstatic that Ingot runs during the build now uploads the static files to the bucket, so builds take longer, and every variable must stay Available during buildtime. With your own Dockerfile, the build has no credentials to upload with: change the Dockerfile’s collectstatic step to RUN python -m compileall -q ., and upload the files in a job instead. On the Processes tab, create a Job named Collect static with the Custom start command python manage.py collectstatic --noinput, keep the Start policy at Before deployment, and deploy. Each deployment’s build log then shows Collect static has been successful before the new version starts. Pages link to the files on the public domain, for example https://storefront-media-q8w2e.sevalla.storage/static/admin/css/base.css. Media files are public too, because public access applies to the whole bucket.
Deploy with the API
This sequence creates the bucket and adds its details to the application from Deploy with the API, and continues in its shell, withAPI and APP_ID set. Run it before you push the code changes from Install django-storages and Configure the storage, because the build needs the variables, and push them before the Deploy step. Each step names the CLI command and the Sevalla MCP server tool that do the same.
1
Create the bucket
Create object storage bucket (It prints the bucket name, such as
createObjectStorageV3) creates a private bucket. location is where most requests come from, here weur (Western Europe) for an application in Frankfurt. The response is the only one that contains the access key and the secret key, so keep it:storefront-media-q8w2e. CLI: sevalla object-storage create --name storefront-media --location weur. MCP: sevalla_api_create with createObjectStorageV3 and the same body.2
Add the environment variables
Create environment variable (CLI:
createEnvironmentVariableV3), once per variable, available during build and runtime:sevalla apps env-vars create "$APP_ID" --key S3_BUCKET --value <bucket name>, once per variable. MCP: set_environment_variables.3
Deploy
Push the code changes, then Trigger deployment (CLI:
triggerDeploymentV3). With Auto-deploy on, the push already started a deployment of the same commit; this one queues behind it:sevalla apps deployments trigger "$APP_ID". MCP: trigger_deployment.4
Wait for the deployment
Get deployment (CLI:
getDeploymentV3) reports the status, which ends as success, failed, cancelled or skipped. The loop keeps polling through a failed request, for up to 30 minutes:sevalla apps deployments get "$DEPLOYMENT_ID" --app-id "$APP_ID". MCP: get_deployment.createObjectStorageDomainV3) turns on public access and returns the public domain in domain, for S3_PUBLIC_DOMAIN.