Dropbox-simple file storage you can self-host in minutes —
and that stays fast even with lots of files.
Holdfast is a self-hosted file storage service you run as a single Docker image (or build yourself into a single static binary). Upload, browse, organize, and share files through a fast, browser-first web dashboard — no desktop app, no sync client, nothing to install on your machine. No external database, no object store, no cloud dependencies — just SQLite and a directory on disk.
![]() |
![]() |
![]() |
![]() |
⚠️ The project is under very active development.⚠️ Expect bugs and breaking changes.⚠️ Do not use Holdfast as the only place you store important files.⚠️ Always follow the 3-2-1 backup plan for data you care about!
For you if:
- You want a Dropbox-like web dashboard for files, self-hosted, with no monthly fee.
- You're comfortable running a Docker container (or a Go binary) on a home server, NAS, or VPS you control.
- You mainly upload, browse, and share files through the browser — on desktop or mobile web.
- You're fine tolerating an early-stage project and keeping real backups.
Not for you if:
- You need a desktop or mobile app that keeps a local folder in sync with the server — Holdfast has no sync client, and none is planned (see Non-goals).
- You need real-time collaborative editing, groupware, or federation.
- You need this to be rock-solid today — the project is under active development and the API/schema can still change between releases.
- Web dashboard — a polished SvelteKit app with grid/list views, drag-and-drop upload, multi-select, keyboard navigation, and full dark mode.
- Resumable uploads — chunked uploads over the tus protocol; interrupted transfers resume from the last good chunk, even for very large files.
- Share links — anonymous, unguessable capability URLs for any file or folder. Recipients need no account; owners can revoke a link at any time.
- Trash with retention — deletes are soft; items are restorable until a configurable background purge removes them for good.
- Previews — thumbnails are generated asynchronously after upload. Images are
handled in-process; video and PDF previews use
ffmpeg/pdftoppmwhen available and degrade gracefully to type icons when not. - Search — instant filename search across your own files and folders.
- Multi-user — an admin bootstraps the instance and creates accounts; every user gets a private file tree. No open registration.
- Self-healing — the database is authoritative and the on-disk tree mirrors it; a background reconcile scan detects and repairs drift between the two.
- Zero-config first run — start the binary, open the browser, create the admin account. Configuration is optional environment variables.
Create a directory and download the two deployment files from the latest release:
mkdir holdfast && cd holdfast
curl -LO https://github.com/shivsarthak/holdfast/releases/latest/download/compose.yaml
curl -L https://github.com/shivsarthak/holdfast/releases/latest/download/env.example -o .env
docker compose up -dOpen http://localhost:8080. On the first boot, Holdfast detects that no Admin exists and redirects you to the one-time setup screen. Create the initial Admin User there; this account can create and manage all other Users. As soon as the Admin is created, bootstrap closes and cannot be used to create another Admin. Subsequent visits show the normal login screen. The named volume persists the database, Files, Trash, and Preview cache across container replacements.
To update later:
docker compose pull
docker compose up -d --remove-orphansFor anything reachable beyond localhost, edit .env and set the public URL to the
HTTPS address users open:
HOLDFAST_PUBLIC_BASE_URL=https://files.example.comBy default, Compose keeps all persistent state in the managed holdfast-data volume.
To store it directly in a host directory, create the directory and set
HOLDFAST_DATA_PATH in .env:
mkdir -p /srv/holdfast/dataThe process inside the container runs as an unprivileged User with UID/GID 10001,
never as root. A bind-mounted host directory keeps its host permissions though, so on
first start the container's entrypoint fixes ownership of the mounted directory (a
one-time step that needs no sudo from you) before dropping to UID 10001. No manual
chown is required.
HOLDFAST_DATA_PATH=/srv/holdfast/dataThe database, Files, Trash, Upload state, and Preview cache will then be visible under
/srv/holdfast/data. See the deployment guide
before converting an existing named-volume installation.
For a compact homelab installation, keep the data beside compose.yaml and .env:
holdfast/
├── compose.yaml
├── .env
└── data/
From inside the holdfast directory:
mkdir dataThen set the relative path in .env:
HOLDFAST_DATA_PATH=./dataCompose resolves ./data relative to the directory containing compose.yaml.
There's no prebuilt binary download — releases currently only publish the container image. Build one yourself; requires Go 1.24+, Node.js 24, and pnpm 10:
make build
./build/holdfastThe binary embeds the frontend and serves everything on http://localhost:8080,
writing state to ./data. Without ffmpeg/pdftoppm on PATH it still runs;
image previews keep working and other formats show type icons.
Holdfast boots with no configuration. Everything is tunable via environment variables:
| Variable | Default | Description |
|---|---|---|
HOLDFAST_PORT |
8080 |
TCP port for the HTTP server. |
HOLDFAST_DATA_DIR |
data |
Directory for the database and all file state. |
HOLDFAST_PUBLIC_BASE_URL |
http://localhost:${PORT} |
Browser-facing URL for share links and cookies. |
HOLDFAST_TRASH_RETENTION |
720h |
How long trashed items stay before purge. |
HOLDFAST_TRASH_PURGE_INTERVAL |
1h |
How often the trash purge runs (0s disables). |
HOLDFAST_RECONCILE_INTERVAL |
1h |
How often reconcile scans run (0s disables). |
See the configuration guide for details and the deployment guide for reverse-proxy (TLS), systemd, Docker Compose, and backup instructions.
| Guide | Contents |
|---|---|
| Configuration | Every environment variable, data directory layout. |
| Deployment | Docker, Compose, systemd, reverse proxies, backups, upgrades. |
| Architecture | How the pieces fit: DB-as-truth, uploads, previews, reconcile. |
| HTTP API | Full endpoint reference and a curl walkthrough. |
| Development | Building, testing, and repository layout for contributors. |
| Releases | Versioning, image tags, prereleases, and the release checklist. |
| Product context | Domain glossary and v1 scope. |
| Frontend | The SvelteKit app: dev server, design system, e2e tests. |
The SQLite database is the source of truth for the file and folder tree; the directory tree under the data directory is a faithful mirror of it. That makes your data transparent — files sit on disk under their real names, ready for standard backup tools — while renames, moves, and shares stay instant and consistent because they are database operations. A background reconcile scan (startup, interval, and on-demand) detects drift between the two and repairs the database to match reality.
data/
holdfast.db SQLite database (authoritative)
files/<user-id>/ mirrored active file/folder tree
uploads/<user-id>/ in-progress resumable upload chunks
trash/<user-id>/ soft-deleted content awaiting purge
previews/<user-id>/ cached preview images
Read more in the architecture guide.
Holdfast deliberately stays small. Out of scope for now: desktop sync clients, groupware, plugins, real-time collaborative editing, federation, folder ZIP download, anonymous "drop box" upload links, quota enforcement, and share-link expiry/passwords. The full scope is recorded in CONTEXT.md.
Contributions are welcome — see CONTRIBUTING.md for how to set up a development environment, run the test suites, and submit changes. Security vulnerabilities should be reported privately to mail@shivsarthak.com (see SECURITY.md) instead of the public tracker.
AGPL-3.0. You can use, modify, and self-host Holdfast freely — but if you run a modified version for others over a network, you must make your modified source available to them. This keeps improvements to Holdfast open for everyone.
Made with ⚓ by shivsarthak




