Skip to content

Persistent Volumes

Apps in Appliku run in containers with an ephemeral filesystem. Application data is not persisted across deploys and restarts — and this works just fine for most apps.

Data is usually stored in a database. For files, the scalable solution is S3-compatible services like AWS S3, DigitalOcean Spaces, etc.

But while S3 is a great solution for bigger apps, it is not convenient for spinning up a quick MVP or viable for some software that requires storing its data in persistent storage.

Volumes are persistent storage that is available for all processes within the app, stays between deploys and restarts, and can be exposed on a URL of your app.

Under the hood it uses Docker volumes pointing to a directory within your application directory (e.g., /home/app/app_name/volumes/volume_123).

Setting Up Volumes

You can add volumes by going to your app's settings, Volumes tab, and click "Add".

Creating Docker volumes

Container Path – the path on which the volume will be available for your running app. It can't be /code or just a slash /.

URL – optional parameter, a subpath on your app's URL where the directory will be exposed to all viewers. This is helpful if you want to store publicly available media files, uploaded by users (e.g., Django media folder).

The URL must follow these rules:

  • It starts with /. After that, use only letters, digits and ., _, +, /, -. Spaces and other characters are refused. The second character cannot be /, and the maximum length is 255 characters.
  • Two volumes of one application cannot have the same location. Appliku ignores the / characters at the end, so /media and /media/ are the same location, and Appliku refuses the second volume with this message: URL "/media" conflicts with volume "..." at location "/media". Use a different path or leave the URL empty.
  • /.well-known/acme-challenge and the paths below it are reserved for the certificate checks of Let's Encrypt.

The value that you enter is kept as it is. For example, with /media/ the _URL environment variable is /media/.

Nginx serves the volume only on the paths below the URL. For example, with the URL /media:

  • /media/photo.jpg gives the file photo.jpg from the volume.
  • /media gives a redirect to /media/.
  • Other paths that start with the same characters, for example /media0/..., /media-kit or /media.json, go to your app. They do not use the volume.

The Nginx configuration of your app changes only when Appliku writes it again, for example on a deploy with "Update Nginx Configuration on deploy" enabled. Before this change, Nginx also used the volume for paths such as /media0/....

Environment variable – an optional parameter that you can specify if you wish to pass the configuration of the paths to your app. Setting this parameter will create two environment variables within the app: one with suffix _ROOT and one with _URL suffix. E.g., setting the value of this parameter to MEDIA will create two environment variables MEDIA_ROOT and MEDIA_URL.

Application Considerations

You can create as many volumes as you need, make some of them available via Nginx and leave some of them private.

When you create, delete, or change parameters of any volumes you must deploy the app for it to take effect.

If you choose to expose the volume on a certain URL, make sure to enable "Update Nginx Configuration on deploy" checkbox on the "Build Settings" tab of app settings.

Update Nginx checkbox

This checkbox will enable rewriting Nginx configuration for all the domains for your app on every deploy. Without enabling this checkbox, changes to volumes-related Nginx configuration will only be applied to newly added custom domains.

Info

Environment variables set by volumes will override any existing ones that you set up in the "Environment Variables" tab during deployment, but won't delete them from that tab.

Upon deletion of a volume, its directory with files is not deleted from the disk.

The same URL rules apply to the volumes section of appliku.yml. If a volume in the file breaks a rule, the deployment fails with the error in its log, and Appliku applies nothing from the file.

Note

A volume that you added before these rules existed can have a URL that breaks them. When Appliku writes the nginx configuration again, it does not serve such a URL:

  • a URL with characters that are not allowed, or a URL in /.well-known/acme-challenge: nginx does not serve the volume at all.
  • two volumes with the same location: nginx serves only the older volume at that location.

The volume stays mounted in the containers, and its environment variables are still set. Change or remove the URL, then deploy.

Serving Pre-Compressed Files

Nginx can send a gzip-compressed copy of a file from a volume that has a URL. Put the compressed copy next to the original file, with the .gz extension added:

gzip -k -9 app.js   # keeps app.js and makes app.js.gz

When a browser asks for app.js and accepts gzip, Nginx sends app.js.gz with Content-Encoding: gzip. Other clients get app.js. Files without a .gz copy are served as usual. Responses for files with a .gz copy include Vary: Accept-Encoding, so caches keep the two versions apart.

Keep in mind:

  • Keep the original file. Clients that do not accept gzip still need it.
  • Nginx does not make or update the .gz files. When you change a file, make its .gz copy again, or Nginx sends the old content.
  • Requests that come through a proxy with a Via header get the original file.
  • This does not apply to apps with a custom Nginx configuration or to apps on clusters.

The Nginx configuration of your app changes only when Appliku writes it again, for example on a deploy with "Update Nginx Configuration on deploy" enabled.

How to Serve Media Files in Django via Nginx

In order to set up your Django project to use a volume for storing and serving media files you should:

  1. Create a volume with URL (/media/) and environment variable set (MEDIA)
  2. In Django project's settings.py file specify two variables:
# with os module
import os
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent

MEDIA_ROOT = os.environ.get('MEDIA_ROOT', BASE_DIR / 'media')
MEDIA_URL = os.environ.get('MEDIA_URL', '/media/')


# or with django-environ
from pathlib import Path
import environ

BASE_DIR = Path(__file__).resolve().parent.parent
MEDIA_ROOT = env('MEDIA_ROOT', default=BASE_DIR / 'media')
MEDIA_URL = env('MEDIA_URL', default='/media/')

Alternatively, you can hardcode paths to your media folder and the URL.