---
title: "Deploy Rusty paste"
description: "Minimal Rust pastebin: one-shot, password, URL shortener, petname links."
category: "Storage"
url: https://railway.com/deploy/rustypaste
---

# Deploy Rusty paste

Minimal Rust pastebin: one-shot, password, URL shortener, petname links.

**[Deploy Rusty paste on Railway](https://railway.com/template/rustypaste)**

Machine-readable deploy manifest (JSON, validated by TemplateCI): https://railway.com/deploy/rustypaste/manifest.json

- **Creator:** mcmax
- **Category:** Storage

## Template content

### rustypaste

- **Source:** https://github.com/mc9max/rustypaste-lite
- **Public domain:** Yes

## Documentation

# 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 to Railway](https://railway.app/button.svg)](https://railway.com/deploy/rustypaste)

## 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 `/data` volume. 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 `expire` header 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.
- **`/data` volume** — 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.
- **`/data` volume** — 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: Bearer` and that password.
- **URL shortener** — returns a 302 redirect to the stored target URL.
- **Petname links** — `capital-mosquito.txt`, not a UUID.
- **Expiry** — per-upload `expire` header or a global default; a janitor sweep removes expired pastes.
- **Upload lock-down** — optional `AUTH_TOKEN` gates 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 `/data` volume 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_path` must be the volume (`/data`, set in `config.toml`) and the volume must be attached; otherwise pastes land in the container's writable layer and are wiped on restart.
- **`401` on upload** — `AUTH_TOKEN` is set; send it as `Authorization: Bearer TOKEN`. Existing pastes are unaffected.
- **`DELETE` returns 404** — `DELETE_TOKEN` is 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_LENGTH` in `config.toml`); raise it and redeploy.

## License

Upstream [rustypaste](https://github.com/orhun/rustypaste) is MIT-licensed. This template's wrapper Dockerfile and config are provided as-is.


## Similar templates

- [Garage S3 Storage](https://railway.com/deploy/garage-s3-storage) — Ultra-light S3 server: fast, open-source, plug-and-play.
- [Redis](https://railway.com/deploy/redis-1) — Self Host Latest Redis with Railway
- [EasyImg](https://railway.com/deploy/easyimg) — Simple self-hostable Nuxt.js personal image hosting system.

Open this page in a browser: https://railway.com/deploy/rustypaste
