UploadServer 2 is a local-first, browser-based file transfer server for Debian and Linux. It gives a private directory a polished drag-and-drop interface for uploading, downloading, searching, sorting, previewing, and navigating files. The primary command is uploadserver2.
The server is designed for practical self-hosting: uploaded content remains under one configured directory, transfers are streamed, and a temporary public Cloudflare Quick Tunnel starts by default unless --no-tunnel is provided.
| Area | Included behavior |
|---|---|
| Transfer | Multiple uploads, drag-and-drop, browser progress, streamed writes, streamed downloads |
| File manager | Recursive folders and files, file type, size, UTC-modified date, search, sort, refresh, empty/loading/error states |
| Transport | Real HTTP/2-capable Hypercorn stack, HTTP/2 over TLS, HTTP/1.1 fallback, h2c support |
| Security | Path traversal defenses, symlink rejection, opaque API identifiers, atomic temporary uploads, no delete operation |
| Access | Optional HTTP Basic Authentication without hard-coded credentials |
| Operations | Debian package, man page, unprivileged systemd service, default cloudflared tunnel, local or remote terminal client |
The primary deliverable is uploadserver2_2.0.0_all.deb.
sudo apt install ./uploadserver2_2.0.0_all.debUsing apt install ./...deb is recommended because apt resolves the package's Debian dependencies automatically. The package bundles its pure-Python runtime dependencies and only requires Debian system Python 3.11 or newer. No virtual environment or manual pip command is required after installation. For an already-installed package with the same version, use sudo apt install --reinstall ./uploadserver2_2.0.0_all.deb.
The same server URL works from either client mode. Start the server normally for the browser, or start it with --console for a local terminal UI; both modes expose the same HTTP service and the same Cloudflare URL. The browser is the easiest choice on a desktop:
cd /path/to/share
uploadserver2On a headless VPS, connect to that URL from another machine without a desktop:
uploadserver2 --console https://your-random-name.trycloudflare.comThe remote console recursively lists folders and files and can stream any file type for download. Press u to upload a local VPS file, d to download the selected remote file, Enter or p to preview remote text/source files, r to refresh, and q to exit. A folder can be selected before pressing u to upload into that folder. For Basic Authentication, use --username and --password, or set UPLOADSERVER2_USERNAME and UPLOADSERVER2_PASSWORD.
uploadserver2 # port 8080, Cloudflare tunnel enabled
uploadserver2 --no-tunnel # local-only port 8080
uploadserver2 -P 9000 --no-tunnel # local-only custom port
uploadserver2 -P 8080 # custom spelling of the default port
uploadserver2 --console --no-tunnel # terminal UI, local-only
uploadserver2 --console URL # terminal client for a remote server
uploadserver2 --help
uploadserver2 --versionWhen you run uploadserver2 interactively, it serves the exact directory you are currently in, just like a simple Python HTTP server. Existing files and every nested subdirectory beneath that directory appear recursively in the web interface. New uploads go to the launch directory by default; select a displayed folder's Upload here action to target a nested directory. To choose another root, pass --directory PATH. The systemd unit explicitly uses /var/lib/uploadserver2/files and runs as the dedicated uploadserver2 user.
The default port is 8080. A normal uploadserver2 launch downloads the official GitHub cloudflared binary to ~/.local/bin/cloudflared if it is not already installed, then prints a temporary public URL. Use --no-tunnel for local-only operation. Add --console to open the terminal UI while the browser server continues running.
Useful options include:
--host HOST listen address
--port PORT listen port
--directory PATH upload directory
--config PATH TOML configuration file
--cert PATH PEM TLS certificate
--key PATH PEM TLS private key
--auth enable Basic Authentication
--max-size SIZE per-file limit, such as 500MB or 10GB
--log-level LEVEL DEBUG, INFO, WARNING, or ERROR
CLI values override the configuration file. A positional port such as uploadserver2 8080 is equivalent to --port 8080.
UploadServer 2 uses a real HTTP/2-capable ASGI server. For browser-friendly HTTPS and HTTP/2, provide both a certificate and private key:
uploadserver2 8443 --cert server.crt --key server.key --directory "$HOME/Uploads"For local development, a self-signed certificate can be generated with OpenSSL:
openssl req -x509 -newkey rsa:2048 -nodes -days 365 \
-keyout server.key -out server.crt -subj '/CN=localhost'Browsers will display a trust warning for a self-signed certificate. Without TLS, the server supports HTTP/1.1 and HTTP/2 cleartext (h2c) for clients that negotiate it; ordinary browsers generally use the HTTP/1.1 fallback.
Authentication protects the GUI, file list, uploads, downloads, previews, and all API routes. Credentials are never hard-coded and passwords are never logged.
Set credentials in the environment and enable authentication:
export UPLOADSERVER2_USERNAME=alice
export UPLOADSERVER2_PASSWORD='use-a-long-random-password'
uploadserver2 8080 --authThey may also be placed in /etc/uploadserver2/config.toml, although environment variables are preferable for secrets. Authentication uses HTTP Basic Authentication, so use TLS when credentials must cross an untrusted network.
Tunnel mode is enabled by default. Cloudflare Quick Tunnels provide a free, temporary public URL for testing without requiring a Cloudflare account. Cloudflare documents the feature at try.cloudflare.com and in its Quick Tunnel documentation. Quick Tunnels are intended for testing and development, not production hosting.
When tunnel mode starts, UploadServer first checks for cloudflared. If it is missing, it downloads the official architecture-matched binary from the Cloudflare GitHub releases to ~/.local/bin/cloudflared, marks it executable, and starts it without requiring root. You can also install it yourself from Cloudflare's official Linux downloads page.
Run UploadServer from the directory you want to share:
cd /path/to/my-files
export UPLOADSERVER2_USERNAME=shareuser
export UPLOADSERVER2_PASSWORD='use-a-long-random-password'
uploadserver2 -P 8080 --authUploadServer runs the official Quick Tunnel command cloudflared tunnel --url http://127.0.0.1:8080, prints the generated https://....trycloudflare.com public URL, and stops cloudflared when the server exits. Anyone with that URL can reach the file server, so use --auth, share the URL carefully, and stop the process when finished. Use --no-tunnel if you only want local access.
The Debian package ships /etc/uploadserver2/config.toml:
[uploadserver2]
host = "127.0.0.1"
port = 8080
directory = "/var/lib/uploadserver2/files"
cert = ""
key = ""
auth = false
username = ""
password = ""
max_size = "10GB"
log_level = "INFO"
tunnel = true
The API never accepts raw filesystem paths. File identifiers are URL-safe opaque encodings of a single filename. Upload names are reduced to a basename, NUL bytes are removed, resolved paths are required to remain below the configured directory, and symlinks are rejected. Uploads land in a random hidden .part file and are atomically renamed only after the complete transfer and size validation. Download responses use file streaming, and the frontend treats names as text rather than injecting untrusted HTML.
Bind to 127.0.0.1 for local use. If binding to all interfaces or using --tunnel, apply authentication, TLS, network controls, and a carefully chosen upload directory. Uploaded files are treated as data and are never executed by UploadServer 2.
The package installs an optional unit but does not enable it automatically. It intentionally passes the Debian configuration explicitly, so the service continues to use /var/lib/uploadserver2/files rather than the invoking user's ~/Uploads directory:
sudoedit /etc/uploadserver2/config.toml
sudo systemctl enable --now uploadserver2.service
journalctl -u uploadserver2.service -fThe service runs as the dedicated unprivileged uploadserver2 user and writes to /var/lib/uploadserver2/files. It is configured to restart after failure, use journald, and avoid broad filesystem writes.
Install Debian packaging tools, then run:
dpkg-buildpackage -us -ucThe resulting package is uploadserver2_2.0.0_all.deb. The source tree contains all Python source, vendored runtime files, maintainer scripts, the systemd unit, man page, default configuration, and .gitignore needed for a reproducible build. No network access or manual pip command is required for the Python runtime dependencies.
From a fresh Debian/Ubuntu build machine:
sudo apt update
sudo apt install -y build-essential debhelper devscripts dpkg-dev python3
cd uploadserver2
dpkg-buildpackage -us -uc
sudo apt install ../uploadserver2_2.0.0_all.debThe Debian package itself declares the system packages needed at install time. The optional first-run cloudflared bootstrap is separate: if cloudflared is missing, UploadServer downloads the architecture-matched official GitHub release into the invoking user's ~/.local/bin directory.
If a port is already in use, choose another port such as uploadserver2 -P 9090. If the configured directory cannot be created or written, select a directory owned by the running user. TLS requires both --cert and --key, and both files must exist. Authentication requires credentials in configuration or UPLOADSERVER2_USERNAME and UPLOADSERVER2_PASSWORD. If the default tunnel cannot connect, check outbound HTTPS and Cloudflare Tunnel connectivity, or run with --no-tunnel for local testing.
For command details, run man uploadserver2 after installation.