Skip to content

junkerderprovinz/featherdrop

Repository files navigation

featherdrop

featherdrop

Build  Lint  Docker Pulls  Image Size  Arch  Next.js  Mantine  Unraid  License


featherdrop is a sleek, feather-light, self-hosted drop zone for your files — drop a file, set how long it lives (plus an optional password or download limit), and share a short link or QR code. Encrypted at rest, resumable uploads, one small container. No accounts, no clouds, no tracking, no nonsense.


Buy me a coffee


Table of Contents

  1. What is this?
  2. How it works
  3. Security & Privacy
  4. Languages
  5. Quick Start on Unraid
  6. Configuration
  7. Reverse Proxy
  8. Local Development
  9. Screenshots
  10. Contributing / License
  11. Support this project

1. What is this?

featherdrop is a sleek, feather-light, self-hosted file-sharing page for your own server — a much simpler take inspired by Pingvin Share. Where Pingvin ships a full backend, database, and accounts, featherdrop is a single container with no login and no separate database:

  • Open the page → a central drop zone is right there.
  • Drop a file → a settings panel slides in (expiry, optional password, optional download limit), and a progress ring overlays the drop zone while it uploads.
  • You get a shareable link plus a QR code you can save as a PNG. The recipient downloads it any time until it expires.
  • Pasting the link into a chat shows a clean preview card — your branding, never the file's name.
  • A light/dark toggle and a flag language picker sit in the header — the UI speaks 26 languages and picks yours from the browser.

Highlights

  • 🔒 Encrypted at rest with age; the filename and type are encrypted inside the file. Optional password shares are end-to-end, and a MASTER_KEY gives short links.
  • Self-destructing — expiry from 1 hour to 30 days (or never), plus an optional burn-after-N-downloads.
  • 🖼️ Inline image/PDF preview and a savable QR code on the share page, with clean link previews that never leak the file's name.
  • 🌍 26 languages (right-to-left for Arabic & Hebrew), light/dark, and custom branding (name, logo, accent colour) via env vars.
  • 📦 One container — resumable uploads (tus), a single SQLite file, separate data/config volumes, multi-arch (amd64 + arm64).
  • 🧹 Private by design — no accounts, no telemetry, no third-party calls at runtime; your files stay on your server.

What it deliberately does not have: user accounts, OIDC/LDAP, email, malware scanning, S3 backends. If you need those, use Pingvin Share — that is the point.


2. How it works

Browser (drop zone, Mantine UI)
   │  resumable upload (tus)
   ▼
featherdrop container (Next.js + small custom Node server)
   ├─ /files            tus upload endpoint
   ├─ /api/finalize     move file into store, write metadata, mint share slug
   ├─ /d/<slug>         share page (info, password gate, download)
   └─ cleanup job       deletes expired files
   ▼
/data volume (uploads, bulk)        /config volume (metadata, small)
   ├─ uploads/<id>   the files          └─ db.sqlite   (better-sqlite3 — a file, not a server)
   └─ tmp/<id>       in-progress uploads

/data (bulk files) and /config (the small SQLite database) are separate volumes, so you can keep uploads on array storage and the database on a fast SSD. CONFIG_DIR defaults to DATA_DIR, so a single-volume setup still works.

Uploads are resumable: a dropped connection on a multi-GB transfer resumes instead of starting over. Passwords are scrypt-hashed, never stored in plain text, and large downloads stream natively (no in-browser buffering).


3. Security & Privacy

featherdrop is built to be self-hosted: your files and their metadata live only on your server, and the app talks to nobody else.

  • No accounts, no tracking. No sign-up, no analytics, no telemetry, and no third-party scripts or fonts pulled at runtime — nothing phones home.
  • Your data stays yours. Uploads sit on your /data volume, metadata in a local SQLite file. Nothing is ever sent to a cloud or external service.
  • Encrypted at rest by default with age — the original filename and type are encrypted inside the file, so a stolen disk or backup reveals neither the contents nor the names (details below).
  • Optional password protection is end-to-end: even you, the operator, cannot read a password-protected share without the password.
  • Self-destructing. Every share has an expiry (down to 1 hour), and an optional download limit burns the file the moment it's reached.
  • Minimal attack surface. No login to brute-force, no user database to leak; share slugs are unguessable, and share pages and link previews never expose the file's name.

Provided under the MIT licence without warranty — you run it, you own the data and the responsibility. Put it behind HTTPS (see Reverse Proxy) and, if you set a MASTER_KEY, keep it safe.

Encryption at rest

Every uploaded file is encrypted at rest by default, using age — a modern, audited, streaming authenticated encryption format. Each file gets its own key, and the original filename and type are encrypted inside the file, so a stolen disk or backup reveals neither the contents nor the names.

How the per-file key is protected depends on whether you set a password, and on whether you've configured a master key:

Share type Where the key lives Link What the server can decrypt
Password Wrapped with your password (age scrypt) …/d/<slug> Nothing without the password — not even the operator
Server (no password, MASTER_KEY set) Wrapped with the server master key …/d/<slug>short The file (it holds the master key); a stolen data backup alone cannot
Link (no password, no master key) In the share link's #fragment …/d/<slug>#k=… — long Nothing from the database alone; the key never reaches the server

Short links. By default a password-less share carries its key in the URL #fragment, which makes the link long but means the server can never decrypt it. If you'd rather have short links (…/d/aB3xK), set a MASTER_KEY (see Configuration): password-less files are then wrapped with it and stored. The trade-off: the running server can decrypt those files — but a stolen /data backup still can't, because the master key lives only in the container environment, not in the volume. Keep it secret and back it up — losing it makes password-less files unrecoverable. Password shares are unaffected and stay end-to-end.

Because the key in a link share lives in the URL fragment, it is never sent in an HTTP request and never appears in server logs or your reverse proxy. Treat the full link as the secret: anyone who has it can download the file until it expires.

Encryption streams (age's 64 KiB authenticated chunks), so multi-GB files are never buffered in memory. Set ENCRYPT_UPLOADS=false to store new uploads as plaintext if you ever need to; files keep the mode they were stored with.


4. Languages

featherdrop's interface ships in 26 languages. On a visitor's first load the language is taken from their browser; a flag picker beside the light/dark toggle (in the header and on the download page) switches it, and the choice is remembered. Detection runs on the server, so the page is already translated before any JavaScript loads. Arabic and Hebrew render right-to-left.

🇬🇧 English · 🇩🇪 Deutsch · 🇫🇷 Français · 🇪🇸 Español · 🇮🇹 Italiano · 🇵🇹 Português · 🇳🇱 Nederlands · 🇵🇱 Polski · 🇷🇺 Русский · 🇺🇦 Українська · 🇨🇿 Čeština · 🇸🇪 Svenska · 🇩🇰 Dansk · 🇫🇮 Suomi · 🇳🇴 Norsk · 🇹🇷 Türkçe · 🇬🇷 Ελληνικά · 🇭🇺 Magyar · 🇷🇴 Română · 🇯🇵 日本語 · 🇰🇷 한국어 · 🇨🇳 中文 · 🇸🇦 العربية · 🇮🇱 עברית · 🇹🇭 ไทย · 🇻🇳 Tiếng Việt

Each language is a typed file under lib/i18n/locales/, with English as the source of truth. A native-speaker correction is a one-file edit — pull requests welcome.


5. Quick Start on Unraid

Pull the template into Unraid via the console / SSH:

mkdir -p /boot/config/plugins/dockerMan/templates-user && \
curl -fsSL -o /boot/config/plugins/dockerMan/templates-user/my-featherdrop.xml \
  https://raw.githubusercontent.com/junkerderprovinz/unraid-docker-templates/main/featherdrop/featherdrop.xml

Then Docker → Add Container → featherdrop under User templates. Map the Data Directory (uploads) and Config Directory (the database) to your appdata, pick a port, hit Apply, open the WebUI.

The template filename must keep the my- prefix (my-featherdrop.xml) so Unraid treats it as a user template.

Plain Docker (no Unraid)

docker run -d \
  --name featherdrop \
  --restart unless-stopped \
  -p 3000:3000 \
  -e BASE_URL=https://share.yourdomain.tld \
  -e CONFIG_DIR=/config \
  -v /mnt/user/appdata/featherdrop/data:/data \
  -v /mnt/user/appdata/featherdrop/config:/config \
  junkerderprovinz/featherdrop:latest

To keep everything on a single volume instead, drop the CONFIG_DIR line and the /config mount and map just -v …:/data — the database then lives in /data alongside the uploads.


6. Configuration

Variable Default Description
BASE_URL (empty) Public URL featherdrop is reached at, so share links use your domain. Empty = use the address the browser is on.
DEFAULT_EXPIRY 7d Expiry pre-selected in the UI. One of 1h, 6h, 1d, 7d, 30d, never.
MAX_FILE_SIZE 0 Max upload size in bytes. 0 = unlimited (disk-limited). E.g. 5368709120 = 5 GB.
ENCRYPT_UPLOADS true Encrypt new uploads at rest with age (see Encryption at rest). Set false to store plaintext. Existing files keep their stored mode.
MASTER_KEY (empty) Optional secret that gives short links for password-less shares (see Encryption at rest). Generate with openssl rand -base64 32. Keep it secret, back it up; losing it makes password-less files unrecoverable. Empty = long #key links.
PORT 3000 Port the server listens on.
DATA_DIR /data Where the uploaded files live (bulk). Map this to a volume.
CONFIG_DIR (= DATA_DIR) Where the SQLite database lives. Defaults to DATA_DIR (single volume). Set it (the Unraid template uses /config) to keep the small database on a separate, faster volume.
APP_NAME featherdrop Custom app name — replaces the wordmark in the header and the browser-tab title.
APP_LOGO (empty) Custom logo (SVG/PNG) replacing the feather: a public image URL, or a data: URI (e.g. data:image/svg+xml;base64,… — generate with base64 -w0 logo.svg) so you need no hosting or file on disk. Empty = the feather.
ACCENT_COLOR #d4af37 A 6-digit hex colour for buttons, the upload ring and accents. Invalid values fall back to the gold.

7. Reverse Proxy

featherdrop speaks plain HTTP on PORT; put TLS in front of it (Nginx Proxy Manager, Caddy, Traefik). Two things matter:

  • Set BASE_URL to your public URL so generated links are correct. Use HTTPS — for link shares the decryption key lives in the URL fragment, and TLS keeps the whole link private in transit.
  • Allow large request bodies and generous timeouts for big uploads. For Nginx / NPM advanced config:
client_max_body_size 0;        # no body-size cap (uploads are chunked anyway)
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_request_buffering off;   # stream uploads straight through

8. Local Development

npm install
npm run dev          # http://localhost:3000, data written to ./data

Build a production bundle and run it the way the container does:

npm run build
npm run start

Run the test suite (pure-logic assertions, no framework needed):

npm test

Stack: Next.js (App Router) + Mantine v7, a small custom Node server (custom-server.ts) that mounts the tus handler beside Next, better-sqlite3 for metadata, and react-i18next for the UI languages. Files live under DATA_DIR (default ./data in dev).


9. Screenshots

featherdrop home — light theme
The home page — drop a file to share it.


featherdrop home — dark theme
Light and dark themes, with a flag language picker.


Upload with share options
Set an expiry, an optional password, and a download limit before sharing.


Share link ready, with QR code
Your link is ready — copy it or save the QR code.


Recipient download page
What the recipient sees: file info and a download button.


10. Contributing / License

Issues and pull requests welcome: https://github.com/junkerderprovinz/featherdrop/issues

Licensed under the MIT License.


11. Support this project

If featherdrop saves you a trip to a third-party file host, consider buying me a coffee:

Buy me a coffee

About

Feather-light, self-hosted file sharing — drop a file, set an expiry, share a link.

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

Packages

 
 
 

Contributors