Deploy Rusty paste
Minimal Rust pastebin: one-shot, password, URL shortener, petname links.
rustypaste
Just deployed
/data
Rustypaste Lite
A ~15 MB single-binary paste service written in Rust (Actix). Four paste kinds — plain file, one-shot, URL shortener, and password-protected — with petname links, auto-expiry, and no database. The whole thing is a filesystem volume. One container, one volume, Hobby plan.
Deploy and Host
One click. Railway provisions a single container with the /data volume and wires the public URL. No database, no sidecars, nothing to configure unless you want to change the default expiry or lock down uploads.
- Runtime — Rust, statically linked, scratch base. No Node, no Python, no JVM.
- Memory — ~15 MB at rest. Fits on the Railway Hobby plan.
- Port — 8000 (baked into the wrapper image).
- Persistence — plain filesystem on the
/datavolume. Pastes survive restarts and redeploys; no database to migrate. - Public URL — auto-generated by Railway; uploads return a full link to YOUR-DOMAIN.
Why Deploy
- Sticky by design. Once GPU logs, bounty notes, and incident traces live in one place with memorable petname links, a second paste tool never gets installed.
- Four paste kinds, one box. Plain file, one-shot (burns after the first read), URL shortener (302 redirect), and password-protected (Argon2id) — all from the same single binary.
- Memorable links. Two-word petname URLs like
capital-mosquito.txt— human-shareable, no UUID soup. - Zero database. Persistence is a filesystem volume. There is nothing to back up, migrate, or corrupt.
- Blazingly small. One ~15 MB static ELF. Deploys in seconds; restarts are instant.
- API-first. Every feature is reachable with plain
curl— no browser required. Scripts and CI can drop long outputs and share the returned URL.
Common Use Cases
- Log capture & share — pipe
kubectl logs, CI output, or a GPU-deal trace into a paste and paste the petname URL next to the incident. - One-shot handoff — share a secret to exactly one person; the link burns the moment they fetch it.
- Password-protected notes — the server generates a strong password and returns it to you in the upload response; the recipient needs it (via
Authorization) to read the file. - Lightweight URL shortener — store a target URL and get a short petname link that 302-redirects to it.
- Expiring drops — per-upload
expireheader or a global default so logs and keys don't linger. - Agent / bot drop — a service (or a human) shares long tracebacks to another endpoint without a UI.
Deployment Dependencies
No external database, cache, or queue:
- Railway Hobby or above — 512 MB RAM is plenty; the binary uses ~15 MB at rest.
/datavolume — where all pastes live; the template attaches it automatically. Without it, pastes vanish on container restart.- No Postgres, no Redis, no object store — everything is a plain file on the volume.
Dependencies for Rustypaste Lite
- Railway Hobby or above — 512 MB RAM is plenty; the binary uses ~15 MB at rest.
/datavolume — required for persistence. The template attaches it automatically.- No external database, cache, or queue — the filesystem on the volume is the storage.
Architecture
One service, one container, one volume:
| Piece | Value |
|---|---|
| Container | orhunp/rustypaste:0.18.1 (scratch base) + root wrapper in this repo |
| Port | 8000 (baked into the wrapper; Railway reverse-proxy + external healthcheck target /) |
| Volume | /data — mounted by Railway; plain-file pastes persist here |
| Lock-down | optional AUTH_TOKEN (gates upload) + DELETE_TOKEN (enables per-file delete) |
| Domain | auto-generated by Railway; exposed at 8000 |
The wrapper runs as uid 0 because Railway mounts persistent volumes as root-owned; the scratch base ships as a non-root user and EACCES-looped on its first write in testing. The scratch image also has no shell, so the healthcheck is Railway's external probe of / (there is no in-container HEALTHCHECK to write). The wrapper pins the config file at /app/config.toml; rustypaste exits at boot without a config, and upload_path is set to /data so every paste lands on the volume.
Features
- Four paste kinds — plain file, one-shot, URL shortener, password-protected — from one binary.
- One-shot (burn after reading) — the link returns the file once, then 404s.
- Password-protected — Argon2id; the server generates the password and returns it in the upload response; read back with
Authorization: Bearerand that password. - URL shortener — returns a 302 redirect to the stored target URL.
- Petname links —
capital-mosquito.txt, not a UUID. - Expiry — per-upload
expireheader or a global default; a janitor sweep removes expired pastes. - Upload lock-down — optional
AUTH_TOKENgates new uploads without hiding existing pastes. - Duplicate detection — identical content re-uploads return the existing link instead of a copy.
- MIME handling — serves each file with a sensible content type from an override table.
About Hosting
Rustypaste Lite is a single container. Railway's reverse proxy terminates TLS at the edge and forwards plain HTTP to the container on port 8000; the external healthcheck targets /. The root wrapper exists solely to make the Railway volume mount writable by the rustypaste process — the binary itself is unchanged from the upstream orhunp/rustypaste:0.18.1 image. Nothing else to host.
Configuration
| Variable | Default | Description |
|---|---|---|
RUST_LOG | info | Logging verbosity: error, warn, info, debug, trace. |
PASTE__DEFAULT_EXPIRY | 1h | Default TTL for each paste when a client sends no expire header. Humantime format (5m, 1h, 1d, 1M). Set to 0 for no default expiry. |
AUTH_TOKEN | blank | When set, new uploads require Authorization: Bearer with this token (401 without). Reading existing pastes stays public. Blank = open public paste. |
DELETE_TOKEN | blank | When set, enables DELETE /{id} for holders of the token. When blank, the delete endpoint is disabled (404) — the safest public posture. |
How to Use
All templates target YOUR-DOMAIN (your Railway public domain).
Plain file paste (petname link back)
curl -F "file=@vast-4090-deal.txt" https://YOUR-DOMAIN/
-> https://YOUR-DOMAIN/capital-mosquito.txt
One-shot paste (burns after the first read)
curl -F "oneshot=@bounty-0xe538.txt" https://YOUR-DOMAIN/
URL shortener (a file whose content is the destination URL) -> 302 redirect
printf 'https://internal.example/trace-12' > target.txt curl -F "url=@target.txt" https://YOUR-DOMAIN/
Password-protected: the response body is two lines — the link, then "Password: ***
curl -F "protected=@secret.txt" https://YOUR-DOMAIN/
Read it back with the generated password (Bearer scheme):
curl -H "Authorization: Bearer GENERATED_PASSWORD" "https://YOUR-DOMAIN/capital-mosquito.txt"
Per-upload expiry (humantime)
curl -F "file=@notes.txt" -H "expire: 24h" https://YOUR-DOMAIN/
Endpoints
| Method | Path | Description |
|---|---|---|
GET | / | Landing page. |
POST | / | Upload. Multipart part name selects the kind: file, oneshot, url, protected. |
GET | /{name} | Fetch a paste. One-shot pastes burn after this read; protected pastes need Authorization: Bearer with the generated password. |
HEAD | /{name} | Metadata only. |
DELETE | /{name} | Delete a paste. Requires a configured DELETE_TOKEN. |
Troubleshooting
- First upload returns EACCES or 500 — the
/datavolume must be attached to the service and the container must run as uid 0 (both are set by this template). A non-root process cannot write Railway's root-owned volume. - Paste gone after a redeploy —
upload_pathmust be the volume (/data, set inconfig.toml) and the volume must be attached; otherwise pastes land in the container's writable layer and are wiped on restart. 401on upload —AUTH_TOKENis set; send it asAuthorization: Bearer TOKEN. Existing pastes are unaffected.DELETEreturns 404 —DELETE_TOKENis not set; the delete endpoint is disabled by design until you set one.- Protected paste can't be read — send the exact password the upload response returned as
Authorization: Bearer GENERATED-PASSWORD; a wrong or missing token returns the same 404 as a missing file (by design, to prevent enumeration). - Paste too large — the wrapper caps uploads at 10 MB (
SERVER__MAX_CONTENT_LENGTHinconfig.toml); raise it and redeploy.
License
Upstream rustypaste is MIT-licensed. This template's wrapper Dockerfile and config are provided as-is.
Template Content
rustypaste
mc9max/rustypaste-lite