Self hosted, easy to install end to end encrypted storage drive
10K+
Hoodik is a lightweight, self-hosted, end-to-end encrypted cloud storage server. All encryption and decryption happens in your browser β the server never sees your plaintext data. Built with Rust (Actix-web) on the backend and Vue 3 on the frontend.
π hoodik.ioβ β Website Β |Β π± Android Appβ Β |Β β‘ VPS Setup Guideβ
Each user gets an RSA-2048 key pair on registration. The private key is stored encrypted with your passphrase β the server cannot read it.
β οΈ Store your private key somewhere safe (e.g. a password manager). If you forget your password, the private key is the only way to recover your account and decrypt your files.
When you upload a file:
Searchable metadata (file name, etc.) is tokenized, hashed, and stored as opaque tokens. When you search, the same operation is applied to your query and the hashes are matched server-side β no plaintext ever leaves the browser.
When you share a file:
https://β¦/links/{id}#link-key.The recipient's browser uses the fragment to decrypt the file key locally. The server only ever sees encrypted bytes.
| Primitive | Algorithm |
|---|---|
| Asymmetric | RSA-2048 PKCS#1 |
| Symmetric (default) | AEGIS-128L β hardware-accelerated AEAD via WASM SIMD128/relaxed-simd |
| Symmetric (supported) | Ascon-128a, ChaCha20-Poly1305 |
| Key derivation | SHA-2, Blake2b |
The cipher used to encrypt each file is stored in the database (files.cipher), so the correct algorithm is always used for decryption regardless of what the current default is.
docker run --name hoodik -d \
-e DATA_DIR='/data' \
-e APP_URL='https://my-app.example.com' \
--volume "$(pwd)/data:/data" \
-p 5443:5443 \
hudik/hoodik:latest
This runs with a self-signed TLS certificate generated automatically in DATA_DIR. For production, provide your own certificate (see Configurationβ ) or put Hoodik behind a reverse proxy such as Nginx Proxy Managerβ .
docker run --name hoodik -d \
-e DATA_DIR='/data' \
-e APP_URL='https://my-app.example.com' \
-e SSL_CERT_FILE='/data/my-cert.crt.pem' \
-e SSL_KEY_FILE='/data/my-key.key.pem' \
-e MAILER_TYPE='smtp' \
-e SMTP_ADDRESS='smtp.gmail.com' \
-e SMTP_USERNAME='[email protected]' \
-e SMTP_PASSWORD='your-app-password' \
-e SMTP_PORT='465' \
-e SMTP_DEFAULT_FROM_EMAIL='[email protected]' \
-e SMTP_DEFAULT_FROM_NAME='Hoodik Drive' \
--volume "$(pwd)/data:/data" \
-p 5443:5443 \
hudik/hoodik:latest
Tip: Set
JWT_SECRETto a stable random string so sessions survive container restarts.
All configuration is done through environment variables. A full reference is in .env.exampleβ .
| Variable | Default | Description |
|---|---|---|
DATA_DIR | (required) | Directory for the database and stored files |
DATABASE_URL | (SQLite) | PostgreSQL connection string β omit to use SQLite |
APP_URL | https://localhost:5443 | Public URL of the application |
APP_CLIENT_URL | APP_URL | URL of the frontend (set to Vite dev server during development) |
HTTP_PORT | 5443 | Port the server listens on |
HTTP_ADDRESS | localhost | Bind address (0.0.0.0 in Docker) |
Database note: SQLite and PostgreSQL databases are not interchangeable. Switching after data has been written will result in data loss.
| Variable | Default | Description |
|---|---|---|
SSL_DISABLED | false | Disable TLS entirely β for development/testing only |
SSL_CERT_FILE | DATA_DIR/hoodik.crt.pem | Path to TLS certificate (auto-generated self-signed cert if missing) |
SSL_KEY_FILE | DATA_DIR/hoodik.key.pem | Path to TLS private key (auto-generated if missing) |
| Variable | Default | Description |
|---|---|---|
JWT_SECRET | (random) | Secret for signing JWTs β set this or all sessions are invalidated on restart |
LONG_TERM_SESSION_DURATION_DAYS | 30 | How many days an idle session stays alive |
SHORT_TERM_SESSION_DURATION_SECONDS | 120 | How many seconds the short-lived access token lives; refreshed automatically while the user is active |
SESSION_COOKIE | hoodik_session | Name of the session cookie |
REFRESH_COOKIE | hoodik_refresh | Name of the refresh token cookie |
COOKIE_HTTP_ONLY | true | Hide the session cookie from JavaScript |
COOKIE_SECURE | true | Only send cookies over HTTPS |
COOKIE_SAME_SITE | Lax | SameSite policy: Lax, Strict, or None |
COOKIE_DOMAIN | (from APP_URL) | Override the cookie domain when your setup requires it |
USE_HEADERS_FOR_AUTHBy default, Hoodik uses HttpOnly cookies for authentication. If your frontend and backend are on different domains (or you want to access the API from a separate app), cookies won't work reliably. Set:
USE_HEADERS_FOR_AUTH=true
With this enabled:
Authorization: Bearer <token> header.Security note: localStorage-based tokens are accessible to any JavaScript on the page (XSS risk). Only enable this when a cookie-based setup is not possible. When using a single domain, leave it at the default
false.
When MAILER_TYPE=none (the default), accounts are activated automatically and no emails are sent. Set MAILER_TYPE=smtp to enable email verification and file-share notifications.
| Variable | Default | Description |
|---|---|---|
MAILER_TYPE | none | smtp to enable email, none to disable |
SMTP_ADDRESS | SMTP server hostname | |
SMTP_USERNAME | SMTP login | |
SMTP_PASSWORD | SMTP password | |
SMTP_PORT | 465 | SMTP port (TLS mode is auto-detected from the port if SMTP_TLS_MODE is not set) |
SMTP_TLS_MODE | (auto) | implicit (port 465), starttls (port 587), or none (port 25) |
SMTP_DEFAULT_FROM_EMAIL | Sender email address | |
SMTP_DEFAULT_FROM_NAME | Sender display name (optional, defaults to Hoodik) |
By default, encrypted file chunks are stored on the local filesystem inside DATA_DIR. Set STORAGE_PROVIDER=s3 to use any S3-compatible object storage instead.
| Variable | Default | Description |
|---|---|---|
STORAGE_PROVIDER | local | local or s3 |
S3_BUCKET | Bucket name | |
S3_REGION | us-east-1 | AWS region |
S3_ENDPOINT | (AWS default) | Custom endpoint for S3-compatible services (MinIO, Backblaze B2, Wasabi, etc.) |
S3_ACCESS_KEY | Access key ID | |
S3_SECRET_KEY | Secret access key | |
S3_PATH_STYLE | false | Path-style addressing (required for MinIO) |
S3_PREFIX | Optional key prefix to namespace objects within a shared bucket |
Note:
DATA_DIRis still required when using S3 β it holds the SQLite database (if not using PostgreSQL) and other local state. Only the encrypted file chunks move to S3.
Example with MinIO:
docker run --name hoodik -d \
-e DATA_DIR='/data' \
-e APP_URL='https://my-app.example.com' \
-e STORAGE_PROVIDER='s3' \
-e S3_BUCKET='hoodik' \
-e S3_ENDPOINT='http://minio:9000' \
-e S3_ACCESS_KEY='minioadmin' \
-e S3_SECRET_KEY='minioadmin' \
-e S3_PATH_STYLE='true' \
--volume "$(pwd)/data:/data" \
-p 5443:5443 \
hudik/hoodik:latest
If you already have data stored locally and want to switch to S3:
Important: Stop the Hoodik server before running the migration to avoid data inconsistencies. If files are being uploaded while chunks are being migrated, some chunks may be missed.
Stop the running Hoodik instance.
Add the S3 environment variables to your docker-compose.yml (keep STORAGE_PROVIDER=local for now).
Run the migration:
docker exec hoodik hoodik migrate-storage
Or as a one-off container:
docker run --rm \
-v hoodik-data:/data \
-e DATA_DIR=/data \
-e S3_BUCKET=my-bucket \
-e S3_REGION=eu-central-1 \
-e S3_ACCESS_KEY=... \
-e S3_SECRET_KEY=... \
hudik/hoodik migrate-storage
The command uploads all chunk files from DATA_DIR to S3. It is idempotent β already-uploaded files are skipped, so it is safe to re-run if interrupted.
Set STORAGE_PROVIDER=s3 and restart:
docker compose up -d
Verify everything works. The local chunk files can be kept as a backup until you are confident.
See DEVELOPMENT.mdβ for setup instructions, available just recipes, testing, and CI.
CC BY-NC 4.0β β free for personal and non-commercial use. For commercial licensing, contact [email protected]β .
Content type
Image
Digest
sha256:8167466eeβ¦
Size
23.7 MB
Last updated
6 days ago
Requires Docker Desktop 4.37.1 or later.