Persistent Volumes: Don't Lose Data on Redeploy

Anything not declared as a volume lives inside the container and disappears on the next deploy — three ways to declare one.

5 min read

Why this matters

A redeploy replaces the container. Anything written to the filesystem inside it — an uploaded file, a SQLite database, a generated cache — is gone the moment the old container is removed, unless it lives in a declared volume instead.

Named Docker volumes

volumes:
  uploads: /app/storage/uploads

Neo creates a Docker volume named <app>-uploads and mounts it at that path. It persists across redeploys, lives entirely in Docker's own storage, and survives even if you don't know or care where on the host filesystem it physically sits.

Bind mounts to a host path

volumes:
  logs: /var/log/myapp:/var/log/app

host:container form — useful when something on the host (a log shipper, a backup script) needs to read the files directly, not just the app.

Structured form

volumes:
  data:
    path: /app/data
    mount: /mnt/ssd/data   # optional — bind to a specific host path, e.g. faster disk

Equivalent to the bind-mount form above, but explicit about which part is the container path and which is the host path — clearer when the paths themselves are long.

What redeploy preserves

On a redeploy, Neo reuses the existing volume for a name it's already seen and only creates new ones for volumes added since the last deploy — so removing a volume from .neo.yml doesn't delete the underlying data, it just stops mounting it. The data stays until you remove it explicitly with neo volumes or Docker directly.

A SQLite gotcha

If your app uses SQLite, the database file itself needs to be inside a declared volume — storage/database.sqlite, not just storage/ in general if the app also writes cache files there you don't want to keep. Be specific about the path that actually needs to survive.

Try it yourself — Neo is free, with every feature unlocked.

Get started free