Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

66 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

uploadserver

A small upload endpoint I run on my home server so ShareX can drop files into a plain folder and get a link back.

The reason it exists is that I was tired of S3 keeping all my uploads as opaque blobs when all I wanted was the real file sitting in a directory I can open and serve myself. ShareX could do this over SFTP, but I'd rather not expose that on a box at home, and a plain HTTP endpoint feels cleaner anyway. So this is the piece in between. It checks a token, writes the upload to disk under a random name, and returns the URL.

It picked up a few things along the way like token auth so I can hand out separate keys, quotas so one key can't eat the whole disk, a built-in file browser with instant search, and web dashboard logins for both admins and upload users to check their usage or browse their files.

Admin Dashboard File Browser & Search
Admin Dashboard File Browser
Token Creation & Management User Profile & Quotas
Token Creation User Profile

Installation

Docker

There's a published image at ghcr.io/skidoodle/uploadserver. Grab compose.yaml from the repo, set the environment how you want, and bring it up:

docker compose up -d

Uploads go to the data volume, tokens to the state volume.

Binary

Releases has prebuilt Linux/amd64 tarballs. Download the latest one, extract, run.

Source

git clone https://github.com/skidoodle/uploadserver
cd uploadserver
go build -o uploadserver .

How to use

The first run prints a root token once. Save it, that's your admin login:

$ uploadserver run
uploadserver: (default root token: Xq3...9fZ - saved to ./state/tokens.db)
uploadserver: listening on :8080, storing uploads in ./data

Make an upload token for ShareX from the dashboard: open the server's URL and log in with the root token. Or make one from the CLI:

$ uploadserver add --label laptop --role upload
created upload token a1b2c3d4
secret (shown once): <your-upload-token>

Then import uploadserver.sxcu into ShareX, set the URL to your domain, and drop the token into the Authorization header. A plain curl does the same job:

$ curl -H "Authorization: Bearer <token>" -F "file=@cat.png" https://u.example.com/
https://u.example.com/a1b2c3d4.png

The link is BASE_URL plus the random filename. uploadserver writes the files but doesn't serve them, so point your web server at the upload folder (nginx, caddy, whatever) and set BASE_URL to wherever that lands.

CLI

$ uploadserver
usage: uploadserver <command> [<args>]

Commands:
  run                        Start the web server
  list                       List all tokens, with usage and quotas
  info <id>                  Show detailed status and usage for a single token
  add [--label L] [--role R] Create a new token
  rm <id>                    Delete a token
  disable <id>               Disable a token
  enable <id>                Enable a token
  limit <id> [flags]         Set upload quotas for a token (use --help for flags)
  global [flags]             Show or set the server-wide default quota
  scan [--token ID]          Find untracked files on disk and optionally import them
  migrate --token <id>       Move flat uploads into per-user directories
  prune [--days N] [--dry-run] Purge temporary upload files older than N days
  export [--out file.json]   Export token store metadata to JSON
  import [--in file.json]    Import token store metadata from JSON
  dump                       Decode the binary store and print everything in it
  reset                      Delete all tokens and reset store
  version                    Show version and runtime info

limit and global take the quota flags:

--total-size 5GB       Lifetime size cap (B/KB/MB/GB/TB; 0 clears it)
--total-uploads 1000   Lifetime upload-count cap
--monthly-size 5GB     Size cap per calendar month
--monthly-uploads 500  Upload-count cap per calendar month

limit also has --bypass to exempt a token from all quotas and --clear to wipe its caps. Only the flags you pass get changed.

Running CLI commands with Docker

You can run CLI commands straight against the running container:

docker compose exec uploadserver scan

If the container is stopped, docker compose run works just as well:

docker compose run --rm uploadserver scan

Scanning for untracked files

If you already have files in the upload directory from a previous setup or manual copy, scan finds them and optionally imports them into a token's history so quotas and the admin dashboard reflect the real state of the disk.

$ uploadserver scan
found 3 untracked file(s) in ./data:

FILE                              SIZE      MODIFIED
a1b2c3d4e5f6.png                  1.2 MB    2025-03-14 09:22
deadbeef1234.jpg                  340 KB    2025-03-15 11:05
cafebabe5678.mp4                  48.7 MB   2025-04-01 16:30

to import these files, re-run with --token <id>

To adopt them into a token:

$ uploadserver scan --token a1b2c3d4
imported 3 file(s) into token a1b2c3d4

Configuration

Everything is set through environment variables:

Variable Description Default
LISTEN_ADDR Address to listen on :8080
UPLOAD_DIR Directory uploads are written to ./data
BASE_URL Public URL prefix for returned links the request's host
ALLOWED_HOSTS Comma-separated list of allowed Host headers for returned URLs when BASE_URL is empty empty (accepts request Host)
UPLOAD_FIELD Multipart form field name file
TOKEN_STORE Path to the bbolt token database ./state/tokens.db
MAX_UPLOAD_BYTES Max upload size in bytes 1073741824 (1 GiB)
RANDOM_NAME_LENGTH Length of the random filename in hex chars 32
STRIP_EXTENSION Drop the file extension from returned URLs false
ENABLE_ADMIN Mount the admin dashboard and API true
SERVE_FILES Serve uploaded files false
TRUST_PROXY_HEADERS Trust X-Forwarded-Proto and X-Forwarded-For from your reverse proxy false
REQUEST_TIMEOUT Deadline for non-upload HTTP responses (Go duration) 30s
PURGE_GRACE_PERIOD Safety delay / grace period before scheduled media purges execute (24h, 7d, or 0s to disable) 24h
CONTROL_SOCKET Control socket path (or off to disable) ./state/control.sock

Leave TRUST_PROXY_HEADERS=false when clients can connect directly. Enable it only when a trusted reverse proxy overwrites forwarded headers. Streamed uploads are excluded from REQUEST_TIMEOUT; MAX_UPLOAD_BYTES and quota reservations bound their request bodies instead.

Releases

Packages

Used by

Contributors

Languages