RoastSlav/quickdrop

QuickDrop is an easy-to-use file sharing application that allows users to upload files without an account, generate download links, and manage file availability, file encryption and optional password protection.

Java

429

132 commits

updated Sep 23, 2026

See the code

See what people are saying

SourceMessageScoreDate

QuickDrop 2.0 — self-hosted file sharing, now with a pastebin, S3/Azure/SFTP/WebDAV storage, and 8 languages (r/selfhosted)

QuickDrop is a small self-hosted file sharing app — drop a file in, get a link out, set an expiry, optionally password-protect and encrypt it. Single container, SQLite, no external services required. 2.0 is by far the biggest release since I started it, so here's what's new. **Pastebin** There's…

0

Sep 23, 2026

README

QuickDrop

Self-hosted file sharing, pastebin and URL shortener. Upload files or paste text, protect them with per-item passwords, hand out expiring share links, and administer everything from a web admin panel that applies changes without a restart.

Build Status Coverage MIT License Docker Pulls


Contents


Features

File uploads

  • Chunked uploads — the browser splits files into 4 MB chunks and the server merges them in the background, so large uploads survive flaky connections.
  • Folder and multi-file uploads — a picked directory, or several files selected at once, is zipped in the browser (ZIP64, stored uncompressed) with a JSON manifest of the original paths and sizes. One upload, one download, and the contents are listed as a tree on the file page, the file list and the share link. A selection that shares a single top-level directory keeps that directory's name (docs.zip); anything else is a dated bundle, files-2692-atbn.zip for 2 September 2026 — the four-character code is added by the server so two bundles from the same day never present the same name.
  • Archive limits: at most 1000 files per selection, and the browser hands over to a streaming ZIP64 writer above 256 MB rather than holding the whole archive in memory. Contents trees longer than 500 lines are collapsed behind a "show more" on the file page.
  • Configurable maximum file size and default retention period; users can renew a file to reset its deletion date.
  • Keep indefinitely exempts a file from scheduled deletion. Hide from list keeps it off the public file list. Either toggle can be restricted to admins.
  • Optional client-side EXIF/metadata stripping for images before upload (files up to 25 MB).

Encryption and passwords

  • Files uploaded with a password are encrypted at rest with AES-256/GCM in 4 MB chunks, keyed by PBKDF2. Files encrypted by older versions (monolithic GCM, and AES-256/CBC from 1.x) are still readable.
  • Per-file passwords are BCrypt-hashed; a successful unlock issues a session token scoped to that file and browser session.
  • Encryption and upload passwords can each be turned off site-wide.

Pastebin

  • Plain-text or Markdown pastes with syntax highlighting in both the editor and the viewer.
  • Password-protected pastes use the same encryption and session flow as files.
  • Edit-only mode — anyone can read the paste, only the password holder can change it.
  • Immutable flag — locks the content permanently.
  • Per-paste view counts and a history log.
  • Pastes are capped at 10 MB, or the configured max file size, whichever is smaller.

File previews

  • Images (PNG, JPEG, GIF, WebP, SVG), plain text and code, PDF, JSON, and CSV/TSV.
  • SVGs are transcoded to PNG server-side with Apache Batik; script execution and external resource loading are disabled during transcoding.
  • Previews above a configurable size require a manual click instead of auto-loading.
  • Generate a share URL for a file without revealing its password.
  • Optional expiry date and download-count cap; expired and exhausted tokens are cleaned up on a schedule.
  • Simplified mode issues unlimited, non-expiring links with a shorter URL.
  • Every share link has a QR code (SVG and PNG).
  • When nothing gates a file — no per-file password and no app password — the share panel offers the page link and a QR code directly instead of the limiter form.
  • For encrypted files, a one-time share key is generated and the file is re-encrypted under it in the background. The key travels in the URL; only its BCrypt hash is stored. Recipients see a "being prepared" page while that runs.
  • Creation, download, expiry and revocation are all recorded in the file's history.

A general-purpose URL shortener at /link/new, sharing the short-code table with share links.

  • Shorten any URL into a /s/{code} link with a QR code; no file involved.
  • Custom aliases — request a specific code instead of a random one. Admin-only by default.
  • Optional expiry date and use-count cap.
  • Destinations are validated on creation and re-validated on every visit: http/https only, and never loopback, link-local or private-network addresses. Admins can add a domain allowlist or blocklist.
  • Optional threat-intelligence checks against Phishing Army, URLhaus and Google Safe Browsing. All three ship disabled and each requires the admin to accept its licence terms in the settings UI first. Safe Browsing only ever receives a truncated hash of the destination.
  • Interstitial preview page showing the destination before continuing — always, never, or non-admins only (default).
  • Per-visit logging can be switched off independently of the link's own use counters.

Storage backends

Files can be stored somewhere other than the server's disk. The backend is chosen in the admin settings and applies without a restart.

BackendConfiguration
Local disk (default)File storage path
S3Endpoint, bucket, region, access/secret key, path-style flag, key prefix
Azure Blob StorageConnection string, container, key prefix
SFTPHost, port, username, password or private key, base path, known-hosts
WebDAVURL, username, password, key prefix
  • Test connection verifies credentials before you commit to a backend.
  • The migration tool at /admin/storage-migration moves existing files between backends, with a preflight check first.
  • Backend health is polled; the UI shows a banner when it is unreachable, and notifications can fire when it goes down and comes back.

Database backups

  • Scheduled backups on a cron expression, keeping the newest N. Off by default.
  • On-demand backup regardless of the schedule.
  • Upload a backup file from elsewhere, so restoring is not limited to backups this instance made.
  • Backups are plain SQLite files produced with VACUUM INTO, so one can be copied over db/quickdrop.db by hand if the app will not start.
  • Download a backup off the server.
  • Every candidate is validated with PRAGMA quick_check before it is allowed to replace the live database.
  • Restoring from the UI stages the file and restarts the app; the restore is applied at boot.
  • Backups, uploads, restores and failures are all logged to the activity log.

Activity log

  • Every file, paste, share, short-link, admin and system event is recorded with timestamp, IP address and user agent.
  • Filterable by date range, event type, IP, user agent and source type.
  • Export the current filter to CSV from the Activity page.
  • Optional retention sweep — per-category age limits (files, pastes, shares, short links, admin, system; 365 days each by default) on a cron schedule. Rows are written to a CSV archive on the configured storage backend under activity-archive/ and only deleted once that archive is committed. Archives are never pruned. Off by default.

Notifications

  • Discord webhook and SMTP email, with per-event toggles for uploads, downloads, renewals, deletions, share creation and download, paste create/view/edit, and storage up/down.
  • Noisy events (share-link downloads, paste views) default to off.
  • Optional batching into a digest at a configurable interval.
  • Test buttons send a live notification from the settings page.

Security

  • Optional site-wide application password.
  • Separate BCrypt-verified admin password, set through a first-run setup flow.
  • Rate limiting: 10 requests per 60-second window per client address on the file-password, admin-password, share, share-download, link-creation and /s/{code} endpoints; over the limit returns HTTP 429 with Retry-After: 60.
  • Cookie-based CSRF protection on state-changing requests; session cookies are HttpOnly and SameSite=Strict.
  • Configurable session timeout; admin and file sessions are tracked and bounded in memory.
  • Outbound URLs (Discord webhook, remote storage backends, short-link destinations) are blocked from resolving to internal network addresses.
  • X-Forwarded-For / X-Real-IP are honoured for client-IP resolution only when the trusted-proxy setting is on.

Branding and feature switches

  • Custom application name and logo.
  • Choose which page / lands on: upload, file list, or new paste.
  • Independently enable or disable uploads, the file list page, the pastebin, share links, the link shortener, encryption, upload passwords, previews and the admin dashboard button. Uploads and the shortener can be made admin-only. Admins keep access to disabled features; other visitors get a service-unavailable page.

Screenshots

image

Upload page — drag-and-drop with configurable size limit, retention period, and encryption status.

image

File view page — download, renew, and generate a share link with expiry and download limits.

image

File list — every non-hidden upload. Can be disabled in the settings.

image

Share link recipient view — download page, no password required.

image

Admin dashboard — aggregate stats for files and pastes, with links to the management pages.

image

Admin settings — applied without a restart.

image

Activity — every event recorded in the app.


Requirements

Docker installDocker with Compose, or any OCI runtime. Images are published for linux/amd64 and linux/arm64.
Manual installJDK 21, Maven 3.9+
Frontend rebuild (optional)Node.js with npm, for the Tailwind CLI
DatabaseNone to install — SQLite, created on first start

Installation

Docker

docker run -d \
  --name quickdrop \
  -p 8080:8080 \
  --restart unless-stopped \
  -v /path/to/db:/app/db \
  -v /path/to/files:/app/files \
  -v /path/to/log:/app/log \
  roastslav/quickdrop:latest

Open http://localhost:8080 and follow the admin setup prompt to set the admin password.

Docker Compose

The repository ships a docker-compose.yml:

docker compose up -d

Build and run manually

git clone https://github.com/RoastSlav/quickdrop.git
./mvnw -B clean package
java -jar target/quickdrop.jar

The app listens on port 8080. The database, files and logs are created relative to the working directory you launch from — see Data directories.


Configuration

Configuration is split in two. A small set of startup properties lives in src/main/resources/application.properties and is fixed once the app starts; everything else is a runtime setting managed from the admin panel and applied immediately.

Startup properties

Override any of these with a JVM argument (-Dserver.port=9000), a command-line argument (--server.port=9000), or the equivalent environment variable using Spring Boot's relaxed binding (SERVER_PORT=9000).

PropertyDefaultPurpose
server.port8080HTTP listen port. Not set in the file; this is Spring Boot's default
spring.datasource.urljdbc:sqlite:db/quickdrop.db?journal_mode=WAL&busy_timeout=5000SQLite location. WAL allows one writer; busy_timeout makes concurrent writers wait instead of failing with SQLITE_BUSY
logging.file.namelog/quickdrop.logApplication log file. Not set in the file — it is derived at startup from the Log storage path runtime setting; setting it explicitly here or on the command line overrides that setting
logging.structured.format.filelogstashThe log file is structured JSON; the console stays human-readable
quickdrop.cors.allowed-origins*Comma-separated origins for credentialed CORS
server.tomcat.max-parameter-count500Raised because the admin settings form posts 100+ fields
server.tomcat.connection-timeout60000Connection timeout, ms
spring.mvc.async.request-timeout3600000Async request timeout, ms — covers long downloads
spring.threads.virtual.enabledtrueTomcat's request executor and Spring's @Async executor run on virtual threads
server.servlet.session.cookie.same-sitestrictSession cookie SameSite
server.servlet.session.cookie.http-onlytrueSession cookie HttpOnly
spring.flyway.baseline-on-migratetrueMigrations run automatically at startup
app.version2.0.0Version shown in the admin About tab

The published image sets no application-specific environment variables of its own.

Data directories

All paths are relative to the process working directory (/app in the image).

PathContentsDeclared as a Docker volume
db/SQLite database quickdrop.db, plus backups/ (database backups) and branding/ (custom logo)Yes
files/reputation-feeds/ (cached Phishing Army and URLhaus feeds), plus — when the storage backend is Local disk — the uploaded files themselves and .upload-chunks/ staging during chunked uploadsYes
log/quickdrop.log. Relocatable via the Log storage path runtime setting, which takes effect on the next restartYes

The db/, files/ and log/ mounts are unchanged. Up to v1.5.3 the custom logo lived in a top-level branding/ directory; anything still there is moved into db/branding/ on startup, so that mount can be dropped.

Runtime settings

Managed at /admin/settings, stored in the database, applied without a restart — the one exception is Log storage path, which the logging system reads before the database is open and so only picks up on the next restart. Grouped as they appear in the settings tabs.

Files

SettingDefaultDescription
Max file size1 GBMaximum upload size
Max file lifetime30 daysAge at which a file becomes eligible for scheduled deletion
File storage pathfilesDirectory used by the Local disk backend
Log storage pathlogDirectory the application log is written to. Read at startup, so a change takes effect on the next restart — unlike every other setting on this page
File deletion cron0 0 2 * * *Schedule for the cleanup job
PreviewsonIn-browser file preview
Max preview size5 MBAbove this, previews need a manual click
Metadata strippingoffStrip EXIF from images in the browser before upload
Keep indefinitely (admin only)offRestrict the keep-indefinitely toggle
Hide from list (admin only)offRestrict the hide toggle

Features

SettingDefaultDescription
Uploads / uploads admin-onlyon / offEnable uploads, or restrict them to admins
File list pageonThe public list at /file/list
PastebinoffThe pastebin feature
Share linksoffShare-token generation
Simplified share linksoffUnlimited, non-expiring links with a shorter URL
Share token length8Characters in a share token
Link shortener / admin-onlyon / offRedirect links at /link/new
Short code length5Characters in a generated short code
Custom aliases / admin-onlyon / onHuman-chosen link codes
Destination preview pageNON_ADMINALWAYS, NEVER, or NON_ADMIN
Domain rule mode / rulesOFF / emptyOFF, allowlist or blocklist for link destinations
Link visit loggingonRecord each short-link visit. Use counters always update
Reputation checkingoffMaster switch for threat-intelligence checks
Phishing Army / URLhaus / Safe BrowsingoffPer-provider, each gated behind licence acceptance
Reputation fail-closedoffBlock a link when a provider check cannot complete, instead of allowing it
Reputation feed cron0 0 4 * * *Feed refresh schedule. Safe Browsing is queried live
URLhaus Auth-Key, Safe Browsing API keyemptyProvider credentials
Admin dashboard buttononShow the Admin link in the nav bar

Security

SettingDefaultDescription
App passwordoffSite-wide access password
Admin passwordunsetSet through the first-run setup flow at /admin/setup
Session lifetime30 minutesHTTP session timeout
EncryptiononAES-256/GCM encryption at rest for password-protected uploads
Upload passwordsonPer-file password support
Trusted reverse proxyoffHonour X-Forwarded-For / X-Real-IP. Only enable behind a real proxy
Activity retentionoffEnable the retention sweep
Activity retention cron0 30 3 * * *Sweep schedule
Retention days per category365 eachFiles, pastes, shares, short links, admin, system

Appearance

SettingDefaultDescription
App nameemptyReplaces "QuickDrop" in the UI and browser tab
LogononeUploaded to db/branding/, replaces the default logo and favicon
Default languageenDefault UI language for new visitors
Default home pageuploadWhere / redirects: upload, list or paste

Notifications

SettingDefaultDescription
Discord webhook / URLoff / emptyWebhook notifications
Email notificationsoffSMTP notifications
SMTP host / port / username / passwordempty / 587SMTP server
SMTP TLS / SSLoff / offSTARTTLS or implicit SSL
Email from / toemptySender and recipients
Notification batching / intervaloffQueue events and send a single digest
Per-event togglesmixedUpload, download, renewal, deletion, share create, paste create, paste edit, storage up and storage down default on; share download and paste view default off

Storage

SettingDefaultDescription
Storage backendLOCALLOCAL, S3, AZURE, SFTP or WEBDAV
S3region us-east-1, key prefix emptyEndpoint, bucket, region, access key, secret key, path-style, key prefix
Azurekey prefix emptyConnection string, container name, key prefix
SFTPport 22, base path /Host, port, username, password or private key, base path, known-hosts
WebDAVkey prefix emptyURL, username, password, key prefix
Scheduled backupsoffAutomatic database backups
Backup cron0 0 4 * * *Backup schedule
Backups to keep7Older backups are pruned after each run

Usage

Upload a file. Go to /file/upload, drag in a file, several files, or a folder — or use the buttons to pick them — optionally set a password and a description, and upload. You get a file page at /file/{uuid} with the download link, the deletion date, and a renew button. Anything but a single file arrives as one zip, with its contents listed on the file page.

Create a paste. Go to /file/paste/new, pick plain text or Markdown, optionally set a password, edit-only mode or the immutable flag, and save.

Share something. On a file or paste page, open the share panel. If the item is gated by a password, you get a share link with an optional expiry date and download cap. If nothing gates it, you get the page link and a QR code instead.

Shorten a URL. Go to /link/new, paste a URL, optionally request a custom alias, an expiry date or a use cap. You get a /s/{code} link and a QR code.

Retrieve something. Open the link. Password-protected items prompt for the password; share links carry their own key and do not.

Key routes:

RoutePurpose
/Redirects to the configured default home page
/file/uploadUpload page
/file/listPublic file list
/file/{uuid}File page
/file/paste/newNew paste
/link/newNew short link
/s/{code}Short-link redirect
/share/{token}Share-link recipient page
/adminAdmin area

Admin panel

Reachable at /admin, protected by its own password. On first run, /admin/setup prompts you to set it; once set, the setup endpoint refuses to overwrite it.

PageRouteWhat it does
Dashboard/admin/dashboardAggregate stats: downloads, storage used, average file size, paste counts and views
Files/admin/filesPaginated, searchable list with delete, hide, extend and keep-indefinitely actions
Pastes/admin/pastesThe same for pastes, with per-paste view counts and history
Links/admin/linksShare links and redirect links, with expiry, remaining uses and a revoke button
Activity/admin/activityEvent log filterable by date, type, IP, user agent and source; CSV export
Backups/admin/backupsCreate, upload, download, restore and delete database backups
Storage migration/admin/storage-migrationMove files between storage backends, with a preflight check
Settings/admin/settingsAll runtime settings

Settings are organised into seven tabs: Appearance, Features, Files, Security, Notifications, Storage and About. Sections with five or more rows collapse by default.

Each file and paste also has a history page listing every event — upload, download, share-link download, renewal, deletion, share creation, expiry and revocation — with timestamp, IP address and user agent.

Scheduled jobs

JobScheduleWhat it does
File cleanupfileDeletionCron, default 0 0 2 * * *Deletes files past maxFileLifeTime, respecting keep-indefinitely
Database reconciliationDaily 03:00Removes rows for files deleted outside the application
Share token cleanupDaily 03:30Deletes expired or exhausted share tokens
Activity retentionactivityRetentionCron, default 0 30 3 * * *Archives and purges old activity rows. Off by default
Database backupbackupCron, default 0 0 4 * * *Creates a backup and prunes to maxBackups. Off by default
Reputation feed refreshreputationFeedCron, default 0 0 4 * * *Re-downloads the Phishing Army and URLhaus feeds. Only runs when those providers are on

Internationalization

Eight languages, selectable per session from the language picker in the navigation bar. The admin sets the default for new visitors.

CodeLanguageBundle
enEnglishmessages.properties
deGermanmessages_de.properties
esSpanishmessages_es.properties
frFrenchmessages_fr.properties
itItalianmessages_it.properties
bgBulgarianmessages_bg.properties
jaJapanesemessages_ja.properties
zhChinese (Simplified)messages_zh.properties

Updating

docker compose pull && docker compose up -d

For a plain docker run deployment, stop and remove the container, pull, and start it again with the same volume mounts:

docker stop quickdrop && docker rm quickdrop && docker pull roastslav/quickdrop:latest

Flyway runs the database migrations at startup. Check the release notes for the version you are moving to before upgrading — some releases need a configuration change as well.


Development

./mvnw -B clean verify
./mvnw -B clean package
npm install && npm run tw:build

verify runs the test suite and enforces a 70% JaCoCo line-coverage gate. tw:build regenerates static/css/tailwind.css from tailwind-input.css.

The running app serves resources from target/classes, so an edit under src/main/resources/static only reaches the browser after a build or:

./mvnw process-resources

Images are published by Jenkins: :develop from the dev branch on every build, and :latest plus :v{version} from master when the pom.xml version changes. Both are multi-arch (linux/amd64, linux/arm64).

TagSource
:latestLatest release from master
:v2.0.0Specific release
:developLatest dev build — may include incomplete features and breaking changes

Contributing

See CONTRIBUTING.md and CODE_OF_CONDUCT.md. Security issues are covered by SECURITY.md.


License

MIT — see LICENSE.

file-sharing
secure-storage
self-hosted

Contributors

RoastSlav

126 commits

issafram

2 commits

bunnypranav

1 commits

danktankk

1 commits

RoastSlav/quickdrop

QuickDrop is an easy-to-use file sharing application that allows users to upload files without an account, generate download links, and manage file availability, file encryption and optional password protection.

Java

429

132 commits

updated Sep 23, 2026

See the code

See what people are saying

SourceMessageScoreDate

QuickDrop 2.0 — self-hosted file sharing, now with a pastebin, S3/Azure/SFTP/WebDAV storage, and 8 languages (r/selfhosted)

QuickDrop is a small self-hosted file sharing app — drop a file in, get a link out, set an expiry, optionally password-protect and encrypt it. Single container, SQLite, no external services required. 2.0 is by far the biggest release since I started it, so here's what's new. **Pastebin** There's…

0

Sep 23, 2026

README

QuickDrop

Self-hosted file sharing, pastebin and URL shortener. Upload files or paste text, protect them with per-item passwords, hand out expiring share links, and administer everything from a web admin panel that applies changes without a restart.

Build Status Coverage MIT License Docker Pulls


Contents


Features

File uploads

  • Chunked uploads — the browser splits files into 4 MB chunks and the server merges them in the background, so large uploads survive flaky connections.
  • Folder and multi-file uploads — a picked directory, or several files selected at once, is zipped in the browser (ZIP64, stored uncompressed) with a JSON manifest of the original paths and sizes. One upload, one download, and the contents are listed as a tree on the file page, the file list and the share link. A selection that shares a single top-level directory keeps that directory's name (docs.zip); anything else is a dated bundle, files-2692-atbn.zip for 2 September 2026 — the four-character code is added by the server so two bundles from the same day never present the same name.
  • Archive limits: at most 1000 files per selection, and the browser hands over to a streaming ZIP64 writer above 256 MB rather than holding the whole archive in memory. Contents trees longer than 500 lines are collapsed behind a "show more" on the file page.
  • Configurable maximum file size and default retention period; users can renew a file to reset its deletion date.
  • Keep indefinitely exempts a file from scheduled deletion. Hide from list keeps it off the public file list. Either toggle can be restricted to admins.
  • Optional client-side EXIF/metadata stripping for images before upload (files up to 25 MB).

Encryption and passwords

  • Files uploaded with a password are encrypted at rest with AES-256/GCM in 4 MB chunks, keyed by PBKDF2. Files encrypted by older versions (monolithic GCM, and AES-256/CBC from 1.x) are still readable.
  • Per-file passwords are BCrypt-hashed; a successful unlock issues a session token scoped to that file and browser session.
  • Encryption and upload passwords can each be turned off site-wide.

Pastebin

  • Plain-text or Markdown pastes with syntax highlighting in both the editor and the viewer.
  • Password-protected pastes use the same encryption and session flow as files.
  • Edit-only mode — anyone can read the paste, only the password holder can change it.
  • Immutable flag — locks the content permanently.
  • Per-paste view counts and a history log.
  • Pastes are capped at 10 MB, or the configured max file size, whichever is smaller.

File previews

  • Images (PNG, JPEG, GIF, WebP, SVG), plain text and code, PDF, JSON, and CSV/TSV.
  • SVGs are transcoded to PNG server-side with Apache Batik; script execution and external resource loading are disabled during transcoding.
  • Previews above a configurable size require a manual click instead of auto-loading.
  • Generate a share URL for a file without revealing its password.
  • Optional expiry date and download-count cap; expired and exhausted tokens are cleaned up on a schedule.
  • Simplified mode issues unlimited, non-expiring links with a shorter URL.
  • Every share link has a QR code (SVG and PNG).
  • When nothing gates a file — no per-file password and no app password — the share panel offers the page link and a QR code directly instead of the limiter form.
  • For encrypted files, a one-time share key is generated and the file is re-encrypted under it in the background. The key travels in the URL; only its BCrypt hash is stored. Recipients see a "being prepared" page while that runs.
  • Creation, download, expiry and revocation are all recorded in the file's history.

A general-purpose URL shortener at /link/new, sharing the short-code table with share links.

  • Shorten any URL into a /s/{code} link with a QR code; no file involved.
  • Custom aliases — request a specific code instead of a random one. Admin-only by default.
  • Optional expiry date and use-count cap.
  • Destinations are validated on creation and re-validated on every visit: http/https only, and never loopback, link-local or private-network addresses. Admins can add a domain allowlist or blocklist.
  • Optional threat-intelligence checks against Phishing Army, URLhaus and Google Safe Browsing. All three ship disabled and each requires the admin to accept its licence terms in the settings UI first. Safe Browsing only ever receives a truncated hash of the destination.
  • Interstitial preview page showing the destination before continuing — always, never, or non-admins only (default).
  • Per-visit logging can be switched off independently of the link's own use counters.

Storage backends

Files can be stored somewhere other than the server's disk. The backend is chosen in the admin settings and applies without a restart.

BackendConfiguration
Local disk (default)File storage path
S3Endpoint, bucket, region, access/secret key, path-style flag, key prefix
Azure Blob StorageConnection string, container, key prefix
SFTPHost, port, username, password or private key, base path, known-hosts
WebDAVURL, username, password, key prefix
  • Test connection verifies credentials before you commit to a backend.
  • The migration tool at /admin/storage-migration moves existing files between backends, with a preflight check first.
  • Backend health is polled; the UI shows a banner when it is unreachable, and notifications can fire when it goes down and comes back.

Database backups

  • Scheduled backups on a cron expression, keeping the newest N. Off by default.
  • On-demand backup regardless of the schedule.
  • Upload a backup file from elsewhere, so restoring is not limited to backups this instance made.
  • Backups are plain SQLite files produced with VACUUM INTO, so one can be copied over db/quickdrop.db by hand if the app will not start.
  • Download a backup off the server.
  • Every candidate is validated with PRAGMA quick_check before it is allowed to replace the live database.
  • Restoring from the UI stages the file and restarts the app; the restore is applied at boot.
  • Backups, uploads, restores and failures are all logged to the activity log.

Activity log

  • Every file, paste, share, short-link, admin and system event is recorded with timestamp, IP address and user agent.
  • Filterable by date range, event type, IP, user agent and source type.
  • Export the current filter to CSV from the Activity page.
  • Optional retention sweep — per-category age limits (files, pastes, shares, short links, admin, system; 365 days each by default) on a cron schedule. Rows are written to a CSV archive on the configured storage backend under activity-archive/ and only deleted once that archive is committed. Archives are never pruned. Off by default.

Notifications

  • Discord webhook and SMTP email, with per-event toggles for uploads, downloads, renewals, deletions, share creation and download, paste create/view/edit, and storage up/down.
  • Noisy events (share-link downloads, paste views) default to off.
  • Optional batching into a digest at a configurable interval.
  • Test buttons send a live notification from the settings page.

Security

  • Optional site-wide application password.
  • Separate BCrypt-verified admin password, set through a first-run setup flow.
  • Rate limiting: 10 requests per 60-second window per client address on the file-password, admin-password, share, share-download, link-creation and /s/{code} endpoints; over the limit returns HTTP 429 with Retry-After: 60.
  • Cookie-based CSRF protection on state-changing requests; session cookies are HttpOnly and SameSite=Strict.
  • Configurable session timeout; admin and file sessions are tracked and bounded in memory.
  • Outbound URLs (Discord webhook, remote storage backends, short-link destinations) are blocked from resolving to internal network addresses.
  • X-Forwarded-For / X-Real-IP are honoured for client-IP resolution only when the trusted-proxy setting is on.

Branding and feature switches

  • Custom application name and logo.
  • Choose which page / lands on: upload, file list, or new paste.
  • Independently enable or disable uploads, the file list page, the pastebin, share links, the link shortener, encryption, upload passwords, previews and the admin dashboard button. Uploads and the shortener can be made admin-only. Admins keep access to disabled features; other visitors get a service-unavailable page.

Screenshots

image

Upload page — drag-and-drop with configurable size limit, retention period, and encryption status.

image

File view page — download, renew, and generate a share link with expiry and download limits.

image

File list — every non-hidden upload. Can be disabled in the settings.

image

Share link recipient view — download page, no password required.

image

Admin dashboard — aggregate stats for files and pastes, with links to the management pages.

image

Admin settings — applied without a restart.

image

Activity — every event recorded in the app.


Requirements

Docker installDocker with Compose, or any OCI runtime. Images are published for linux/amd64 and linux/arm64.
Manual installJDK 21, Maven 3.9+
Frontend rebuild (optional)Node.js with npm, for the Tailwind CLI
DatabaseNone to install — SQLite, created on first start

Installation

Docker

docker run -d \
  --name quickdrop \
  -p 8080:8080 \
  --restart unless-stopped \
  -v /path/to/db:/app/db \
  -v /path/to/files:/app/files \
  -v /path/to/log:/app/log \
  roastslav/quickdrop:latest

Open http://localhost:8080 and follow the admin setup prompt to set the admin password.

Docker Compose

The repository ships a docker-compose.yml:

docker compose up -d

Build and run manually

git clone https://github.com/RoastSlav/quickdrop.git
./mvnw -B clean package
java -jar target/quickdrop.jar

The app listens on port 8080. The database, files and logs are created relative to the working directory you launch from — see Data directories.


Configuration

Configuration is split in two. A small set of startup properties lives in src/main/resources/application.properties and is fixed once the app starts; everything else is a runtime setting managed from the admin panel and applied immediately.

Startup properties

Override any of these with a JVM argument (-Dserver.port=9000), a command-line argument (--server.port=9000), or the equivalent environment variable using Spring Boot's relaxed binding (SERVER_PORT=9000).

PropertyDefaultPurpose
server.port8080HTTP listen port. Not set in the file; this is Spring Boot's default
spring.datasource.urljdbc:sqlite:db/quickdrop.db?journal_mode=WAL&busy_timeout=5000SQLite location. WAL allows one writer; busy_timeout makes concurrent writers wait instead of failing with SQLITE_BUSY
logging.file.namelog/quickdrop.logApplication log file. Not set in the file — it is derived at startup from the Log storage path runtime setting; setting it explicitly here or on the command line overrides that setting
logging.structured.format.filelogstashThe log file is structured JSON; the console stays human-readable
quickdrop.cors.allowed-origins*Comma-separated origins for credentialed CORS
server.tomcat.max-parameter-count500Raised because the admin settings form posts 100+ fields
server.tomcat.connection-timeout60000Connection timeout, ms
spring.mvc.async.request-timeout3600000Async request timeout, ms — covers long downloads
spring.threads.virtual.enabledtrueTomcat's request executor and Spring's @Async executor run on virtual threads
server.servlet.session.cookie.same-sitestrictSession cookie SameSite
server.servlet.session.cookie.http-onlytrueSession cookie HttpOnly
spring.flyway.baseline-on-migratetrueMigrations run automatically at startup
app.version2.0.0Version shown in the admin About tab

The published image sets no application-specific environment variables of its own.

Data directories

All paths are relative to the process working directory (/app in the image).

PathContentsDeclared as a Docker volume
db/SQLite database quickdrop.db, plus backups/ (database backups) and branding/ (custom logo)Yes
files/reputation-feeds/ (cached Phishing Army and URLhaus feeds), plus — when the storage backend is Local disk — the uploaded files themselves and .upload-chunks/ staging during chunked uploadsYes
log/quickdrop.log. Relocatable via the Log storage path runtime setting, which takes effect on the next restartYes

The db/, files/ and log/ mounts are unchanged. Up to v1.5.3 the custom logo lived in a top-level branding/ directory; anything still there is moved into db/branding/ on startup, so that mount can be dropped.

Runtime settings

Managed at /admin/settings, stored in the database, applied without a restart — the one exception is Log storage path, which the logging system reads before the database is open and so only picks up on the next restart. Grouped as they appear in the settings tabs.

Files

SettingDefaultDescription
Max file size1 GBMaximum upload size
Max file lifetime30 daysAge at which a file becomes eligible for scheduled deletion
File storage pathfilesDirectory used by the Local disk backend
Log storage pathlogDirectory the application log is written to. Read at startup, so a change takes effect on the next restart — unlike every other setting on this page
File deletion cron0 0 2 * * *Schedule for the cleanup job
PreviewsonIn-browser file preview
Max preview size5 MBAbove this, previews need a manual click
Metadata strippingoffStrip EXIF from images in the browser before upload
Keep indefinitely (admin only)offRestrict the keep-indefinitely toggle
Hide from list (admin only)offRestrict the hide toggle

Features

SettingDefaultDescription
Uploads / uploads admin-onlyon / offEnable uploads, or restrict them to admins
File list pageonThe public list at /file/list
PastebinoffThe pastebin feature
Share linksoffShare-token generation
Simplified share linksoffUnlimited, non-expiring links with a shorter URL
Share token length8Characters in a share token
Link shortener / admin-onlyon / offRedirect links at /link/new
Short code length5Characters in a generated short code
Custom aliases / admin-onlyon / onHuman-chosen link codes
Destination preview pageNON_ADMINALWAYS, NEVER, or NON_ADMIN
Domain rule mode / rulesOFF / emptyOFF, allowlist or blocklist for link destinations
Link visit loggingonRecord each short-link visit. Use counters always update
Reputation checkingoffMaster switch for threat-intelligence checks
Phishing Army / URLhaus / Safe BrowsingoffPer-provider, each gated behind licence acceptance
Reputation fail-closedoffBlock a link when a provider check cannot complete, instead of allowing it
Reputation feed cron0 0 4 * * *Feed refresh schedule. Safe Browsing is queried live
URLhaus Auth-Key, Safe Browsing API keyemptyProvider credentials
Admin dashboard buttononShow the Admin link in the nav bar

Security

SettingDefaultDescription
App passwordoffSite-wide access password
Admin passwordunsetSet through the first-run setup flow at /admin/setup
Session lifetime30 minutesHTTP session timeout
EncryptiononAES-256/GCM encryption at rest for password-protected uploads
Upload passwordsonPer-file password support
Trusted reverse proxyoffHonour X-Forwarded-For / X-Real-IP. Only enable behind a real proxy
Activity retentionoffEnable the retention sweep
Activity retention cron0 30 3 * * *Sweep schedule
Retention days per category365 eachFiles, pastes, shares, short links, admin, system

Appearance

SettingDefaultDescription
App nameemptyReplaces "QuickDrop" in the UI and browser tab
LogononeUploaded to db/branding/, replaces the default logo and favicon
Default languageenDefault UI language for new visitors
Default home pageuploadWhere / redirects: upload, list or paste

Notifications

SettingDefaultDescription
Discord webhook / URLoff / emptyWebhook notifications
Email notificationsoffSMTP notifications
SMTP host / port / username / passwordempty / 587SMTP server
SMTP TLS / SSLoff / offSTARTTLS or implicit SSL
Email from / toemptySender and recipients
Notification batching / intervaloffQueue events and send a single digest
Per-event togglesmixedUpload, download, renewal, deletion, share create, paste create, paste edit, storage up and storage down default on; share download and paste view default off

Storage

SettingDefaultDescription
Storage backendLOCALLOCAL, S3, AZURE, SFTP or WEBDAV
S3region us-east-1, key prefix emptyEndpoint, bucket, region, access key, secret key, path-style, key prefix
Azurekey prefix emptyConnection string, container name, key prefix
SFTPport 22, base path /Host, port, username, password or private key, base path, known-hosts
WebDAVkey prefix emptyURL, username, password, key prefix
Scheduled backupsoffAutomatic database backups
Backup cron0 0 4 * * *Backup schedule
Backups to keep7Older backups are pruned after each run

Usage

Upload a file. Go to /file/upload, drag in a file, several files, or a folder — or use the buttons to pick them — optionally set a password and a description, and upload. You get a file page at /file/{uuid} with the download link, the deletion date, and a renew button. Anything but a single file arrives as one zip, with its contents listed on the file page.

Create a paste. Go to /file/paste/new, pick plain text or Markdown, optionally set a password, edit-only mode or the immutable flag, and save.

Share something. On a file or paste page, open the share panel. If the item is gated by a password, you get a share link with an optional expiry date and download cap. If nothing gates it, you get the page link and a QR code instead.

Shorten a URL. Go to /link/new, paste a URL, optionally request a custom alias, an expiry date or a use cap. You get a /s/{code} link and a QR code.

Retrieve something. Open the link. Password-protected items prompt for the password; share links carry their own key and do not.

Key routes:

RoutePurpose
/Redirects to the configured default home page
/file/uploadUpload page
/file/listPublic file list
/file/{uuid}File page
/file/paste/newNew paste
/link/newNew short link
/s/{code}Short-link redirect
/share/{token}Share-link recipient page
/adminAdmin area

Admin panel

Reachable at /admin, protected by its own password. On first run, /admin/setup prompts you to set it; once set, the setup endpoint refuses to overwrite it.

PageRouteWhat it does
Dashboard/admin/dashboardAggregate stats: downloads, storage used, average file size, paste counts and views
Files/admin/filesPaginated, searchable list with delete, hide, extend and keep-indefinitely actions
Pastes/admin/pastesThe same for pastes, with per-paste view counts and history
Links/admin/linksShare links and redirect links, with expiry, remaining uses and a revoke button
Activity/admin/activityEvent log filterable by date, type, IP, user agent and source; CSV export
Backups/admin/backupsCreate, upload, download, restore and delete database backups
Storage migration/admin/storage-migrationMove files between storage backends, with a preflight check
Settings/admin/settingsAll runtime settings

Settings are organised into seven tabs: Appearance, Features, Files, Security, Notifications, Storage and About. Sections with five or more rows collapse by default.

Each file and paste also has a history page listing every event — upload, download, share-link download, renewal, deletion, share creation, expiry and revocation — with timestamp, IP address and user agent.

Scheduled jobs

JobScheduleWhat it does
File cleanupfileDeletionCron, default 0 0 2 * * *Deletes files past maxFileLifeTime, respecting keep-indefinitely
Database reconciliationDaily 03:00Removes rows for files deleted outside the application
Share token cleanupDaily 03:30Deletes expired or exhausted share tokens
Activity retentionactivityRetentionCron, default 0 30 3 * * *Archives and purges old activity rows. Off by default
Database backupbackupCron, default 0 0 4 * * *Creates a backup and prunes to maxBackups. Off by default
Reputation feed refreshreputationFeedCron, default 0 0 4 * * *Re-downloads the Phishing Army and URLhaus feeds. Only runs when those providers are on

Internationalization

Eight languages, selectable per session from the language picker in the navigation bar. The admin sets the default for new visitors.

CodeLanguageBundle
enEnglishmessages.properties
deGermanmessages_de.properties
esSpanishmessages_es.properties
frFrenchmessages_fr.properties
itItalianmessages_it.properties
bgBulgarianmessages_bg.properties
jaJapanesemessages_ja.properties
zhChinese (Simplified)messages_zh.properties

Updating

docker compose pull && docker compose up -d

For a plain docker run deployment, stop and remove the container, pull, and start it again with the same volume mounts:

docker stop quickdrop && docker rm quickdrop && docker pull roastslav/quickdrop:latest

Flyway runs the database migrations at startup. Check the release notes for the version you are moving to before upgrading — some releases need a configuration change as well.


Development

./mvnw -B clean verify
./mvnw -B clean package
npm install && npm run tw:build

verify runs the test suite and enforces a 70% JaCoCo line-coverage gate. tw:build regenerates static/css/tailwind.css from tailwind-input.css.

The running app serves resources from target/classes, so an edit under src/main/resources/static only reaches the browser after a build or:

./mvnw process-resources

Images are published by Jenkins: :develop from the dev branch on every build, and :latest plus :v{version} from master when the pom.xml version changes. Both are multi-arch (linux/amd64, linux/arm64).

TagSource
:latestLatest release from master
:v2.0.0Specific release
:developLatest dev build — may include incomplete features and breaking changes

Contributing

See CONTRIBUTING.md and CODE_OF_CONDUCT.md. Security issues are covered by SECURITY.md.


License

MIT — see LICENSE.

file-sharing
secure-storage
self-hosted

Contributors

RoastSlav

126 commits

issafram

2 commits

bunnypranav

1 commits

danktankk

1 commits

Languages

Java

59.1%

HTML

23.5%

JavaScript

11.4%

CSS

5.2%