curl-based file sharing service
English | Korean
For CLI packages and usage, see pastebox-cli.
For upload, retrieval, clone, and management endpoints, see the English HTTP API reference.
| Layer | Stack |
|---|---|
| OS | Alpine Linux 3.24.1 (mirror: https://mirror5.krfoss.org/alpine) |
| Language | Go 1.26.4 |
| Frontend | Go HTML Template |
| Backend | Go Standard Library HTTP Server |
| Storage | Local / MySQL & MariaDB |
If there is a specific mirror you want to use, you can modify it in the Dockerfile.
pastebox/
├── .github/
│ └── workflows/
│ ├── docker-publish.yml
│ └── release.yml
├── AGENTS.md
├── API.md
├── API_ko.md
├── DATA_POLICY.md
├── DATA_POLICY_ko.md
├── Dockerfile
├── LICENSE
├── README.md
├── README_ko.md
├── 404.png
├── admin.png
├── docker-compose.yml
├── docker-entrypoint.sh
├── go.mod
├── go.sum
├── main.png
├── paste.png
├── password-protected.png
├── cmd/
│ └── server/
│ ├── admin.go
│ ├── app.go
│ ├── auth.go
│ ├── handler_clone.go
│ ├── handler_index.go
│ ├── handler_manage.go
│ ├── handler_paste.go
│ ├── handler_upload.go
│ ├── i18n.go
│ ├── logging.go
│ ├── main.go
│ ├── main_test.go
│ ├── routes.go
│ ├── upload_validation.go
│ └── util.go
├── internal/
│ ├── admin_store.go
│ ├── locks.go
│ ├── metadata.go
│ ├── mysql_store.go
│ ├── secrets.go
│ ├── store.go
│ ├── store_local.go
│ └── store_test.go
├── locales/
│ ├── en.json
│ └── ko.json
└── templates/
├── 404.html
├── admin_form.html
├── admin_list.html
├── admin_reset.html
├── clone.html
├── index.html
├── manage.html
├── password.html
└── paste.html
Important
The default time zone is set to Asia/Seoul. Please set it to match the country where you currently reside.
- Clone the repository or download it as a .zip file.
- Run the service using Docker Compose. You can build and run it locally with
docker compose up -d --build, or use the prebuilt image from GHCR. To use the prebuilt image, run it withdocker-compose.yml. - Open
http://localhost:3000in your browser, or access the service through a reverse proxy configured with Nginx, Traefik, or Caddy. Once the service is running properly, you can use it withcurl.
Pastebox exposes GET /healthz and HEAD /healthz for container and load balancer health checks.
- A healthy response returns
200 OKwithok. - An unhealthy response returns
503 Service Unavailable. - The check always verifies the admin SQLite database.
- When
STORAGE_BACKEND=mysql, it also verifies the configured MySQL or MariaDB connection. - Uploads being disabled does not make the service unhealthy.
The provided Compose files already include a Docker health check that probes http://127.0.0.1:8080/healthz.
Example:
curl -i http://localhost:8080/healthzWhen the database becomes unavailable after startup, Pastebox also writes state-transition logs to the container output. It records the first failure and the recovery event, rather than logging every probe failure repeatedly.
Typical log messages:
store health check failed: backend=mysql error=dial tcp 10.0.0.10:3306: connect: connection refused
store health recovered: backend=mysql
Pastebox supports local and mysql storage backends. Use local for local file storage, or use mysql for an external MySQL & MariaDB database.
| Variable | Default | Description |
|---|---|---|
STORAGE_BACKEND |
local |
Paste storage backend. Use local for /paste-data files or mysql for an external MySQL & MariaDB database. |
MYSQL_DSN |
empty | Required when STORAGE_BACKEND=mysql. Keep parseTime=true and utf8mb4 options for compatibility. |
DB_ZSTD_LEVEL |
3 |
Optional zstd compression level for DB-mode paste content. |
MIGRATE_LOCAL_PASTES |
false |
When STORAGE_BACKEND=mysql, migrate existing local paste files into MySQL during startup. This is a one-time migration: once it succeeds, Pastebox records completion in SQLite and skips it on later restarts. |
MIGRATE_SQLITE_ADMIN_ACCOUNTS |
false |
When STORAGE_BACKEND=mysql, migrate the existing SQLite admin account, admin sessions, and admin settings into MySQL during startup. This is a one-time migration: once it succeeds, Pastebox records completion in SQLite and skips it on later restarts. |
Example MySQL & MariaDB configuration:
environment:
STORAGE_BACKEND: "mysql"
MYSQL_DSN: "pastebox:pastebox@tcp(mysql.example.com:3306)/pastebox?parseTime=true&charset=utf8mb4&collation=utf8mb4_unicode_ci"
DB_ZSTD_LEVEL: "3"
MIGRATE_LOCAL_PASTES: "true"
MIGRATE_SQLITE_ADMIN_ACCOUNTS: "true"In local mode, paste content is stored under DATA_DIR with JSON sidecar metadata. In mysql mode, paste content, the admin account, admin sessions, and admin settings are stored in the configured MySQL or MariaDB database. SQLite at /paste-data/pastebox.db remains in use only for migration completion markers.
When using a bind mount such as ./data:/paste-data, set UID and GID to the host user's numeric IDs if you want the host account to keep direct access to the stored files. For example, UID=1000 and GID=1000 make the container write data as that host user instead of leaving the bind-mounted directory owned only by container root.
The two migration flags are startup-only helpers for moving an existing local installation into MySQL:
MIGRATE_LOCAL_PASTES=truescansDATA_DIRfor local paste files and their JSON sidecars, copies each paste into MySQL, then removes the local file pair after each successful copy.MIGRATE_SQLITE_ADMIN_ACCOUNTS=truecopies the existing SQLite admin account, admin sessions, and admin settings into MySQL, then removes the migrated SQLite rows after success. Migration completion markers remain in SQLite.
Both migrations are protected by a completion marker stored in SQLite under pastebox_settings. After a migration finishes successfully, later restarts skip it. If a migration fails before the marker is written, Pastebox will try again on the next startup.
Note
DON'T FORGET TO REPLACE localhost WITH THE DOMAIN OR IP ADDRESS YOU'RE CURRENTLY USING.
-
Automatic File Deletion: Uploaded files are automatically deleted 30 days after upload.
-
Text Upload: You can upload text directly by combining Pastebox with Linux commands such as echo and cat (cat << EOF).
echo "hello" | curl -X POST --data-binary @- http://localhost:8080/
-
File Upload: Supports file uploads using the
multipart/form-dataformat.curl -F "file=@test.txt" http://localhost:8080/ -
Permanent Storage: Use the
data-policy: permanentheader to exclude an uploaded file from automatic deletion and store it permanently.curl -H "data-policy: permanent" -F "file=@test.txt" http://localhost:8080/
// Storage path: ./data/code.json { "id": "code", "created_at": "2026-05-25T06:46:51.108540924Z", "expires_at": "0001-01-01T00:00:00Z", "data_policy": "permanent", "size": 5, "content_type": "application/octet-stream" }
-
One-time storage: When the
data-policy: onceheader is used, the data is stored only once and is automatically deleted once the user has confirmed it.curl -H "data-policy: once" -F "file=@test.txt" http://localhost:8080/
{ "id": "code", "delete_token_hash": "yourDeleteToken", "created_at": "2026-05-26T11:11:09.799454368Z", "expires_at": "2026-06-25T11:11:09.799454368Z", "data_policy": "once", "size": 6, "content_type": "text/plain; charset=utf-8" } -
Custom Expiration: Use
data-policyvalues such as30m,12h, or7dto set a custom expiration up to 30 days. The suffix must bemfor minutes,hfor hours, ordfor days. Invalid values such as0m,31d,721h,1w, or1.5hare rejected with400 Bad Request.curl -H "data-policy: 12h" -F "file=@test.txt" http://localhost:8080/
-
Expiration Information: Temporary and custom-duration uploads include an
expiresfield in the response so you can check when the file will expire. The response timestamp is formatted in the server's local timezone. Ifdata-policy: permanentis used, the expiration date is not shown.url: http://localhost:8080/RANDOM_CODE expires: 2026-06-24T14:10:26+09:00 manage: http://localhost:8080/RANDOM_CODE?manage=MANAGE_TOKEN -
Manual Deletion: Open the private manage URL returned by the upload and use its delete button. The manage page sends an authenticated
DELETErequest to the management API. CLI clients can make the same request with the manage token. Deletion requests are also recorded in the container logs.curl -X DELETE -H "paste-manage-token: MANAGE_TOKEN" http://localhost:8080/api/v1/pastes/RANDOM_CODE -
Password-Protected Links: Private upload link creation using the
usepassword: trueheader is supported. When this header is used, an 8-character password is issued, generated from a combination of uppercase English letters, lowercase English letters, numbers, and special characters. Files can be viewed directly using the?password=...query parameter or thepaste-password: ...header, or by entering the password manually when accessing the link in a browser.# Create password-protected link curl -H "usepassword: true" -F "file=@secret.txt" http://localhost:8080/ # View file: header method curl -H "paste-password: RANDOM_PASSWORD" http://localhost:8080/RANDOM_CODE # View file: query parameter method curl "http://localhost:8080/RANDOM_CODE?password=RANDOM_PASSWORD"
Interactive clients may instead supply an 8-128 character password in the
passwordheader. Do not combine it withusepassword: true. The supplied password is stored only as a hash and is not echoed in the upload response. JSON upload and clone responses includepassword_protectedso clients can verify that protection was applied. -
Custom Code: You can use the
code: ...header to create a link with a code of your choice instead of a randomly generated code. Uppercase and lowercase English letters, numbers, and the special characters_and-are supported. Codes longer than 10 characters or duplicate codes cannot be created.
curl -H "code: custom123" -F "file=@secret.txt" http://localhost:8080/Use the optional label: ... header to identify a paste independently from its code and original filename. Labels may contain up to 100 characters, are preserved when cloning, and can be changed from the private manage page.
curl -H "label: production deploy log" -F "file=@server.log" http://localhost:8080/- Upload Response Format: When an upload succeeds, Pastebox returns the URL, expiration time, and private manage link. If the upload is password-protected with a generated password, the
passwordfield is also included.
url: http://localhost:8080/RANDOM_CODE
expires: 2026-06-24T14:10:26+09:00
password: RANDOM_PASSWORD
manage: http://localhost:8080/RANDOM_CODE?manage=MANAGE_TOKEN
If you need a JSON response for parsing by scripts or other tools, append ?format=json to the upload request:
curl -F "file=@test.txt" "http://localhost:8080/?format=json"{
"url": "http://localhost:8080/RANDOM_CODE",
"expires": "2026-06-24T14:10:26+09:00",
"password": "RANDOM_PASSWORD",
"manage": "http://localhost:8080/RANDOM_CODE?manage=MANAGE_TOKEN",
"password_protected": true
}- Paste Management Link: Each successful upload now also generates a private management URL. The link is delivered as a query parameter in the upload response using
?manage=.... Anyone with that token can access the management page directly without entering a password. Later requests must continue to include the samemanagetoken in the URL.
The management link can be used to:
- switch between public and password-protected access
- change the retention policy
- delete the paste
When converting a password-protected paste back to public, Pastebox requires the current generated password for verification first.
CLI and automation clients can use the versioned JSON API at /api/v1/pastes/<code>. Send the private manage token in the paste-manage-token header; tokens are never returned by this API. GET returns management metadata, PATCH accepts the actions set_label, set_policy, enable_password, and disable_password, and DELETE removes the paste.
curl -H "paste-manage-token: MANAGE_TOKEN" http://localhost:8080/api/v1/pastes/RANDOM_CODE
curl -X PATCH -H "Content-Type: application/json" -H "paste-manage-token: MANAGE_TOKEN" --data '{"action":"set_policy","data_policy":"12h"}' http://localhost:8080/api/v1/pastes/RANDOM_CODE
curl -X DELETE -H "paste-manage-token: MANAGE_TOKEN" http://localhost:8080/api/v1/pastes/RANDOM_CODE-
Copy Content in Browser: When opening a text-based upload link in the browser, you can copy the content to your clipboard using the
Copybutton next to theRawbutton. -
Text File Rendering in Browser: Text-based files such as
.txtand.logare displayed directly in the browser instead of being downloaded. If you need the original raw response, use?format=raw. -
Creation and Deletion Logs: File creation and deletion events are recorded in the container logs.
created: id=AbC12 remote=127.0.0.1:51234 size=123 content_type="text/plain; charset=utf-8" policy=temporary expires=2026-06-24T05:10:26Z protected=false
deleted: id=AbC12 remote=127.0.0.1:51234
-
Fine-Grained Lock Manager: Pastebox applies locks per upload ID to reduce conflicts when viewing, deleting, or cleaning up the same file concurrently. Different files can still be processed in parallel.
-
Admin Page: You can access the admin page by adding
/adminafter the IP address or domain. If no account exists, the first created account becomes the administrator account, and additional account creation is disabled afterward. WhenSTORAGE_BACKEND=mysql, the admin account, admin sessions, and admin settings are stored in MySQL. SQLite at/paste-data/pastebox.dbinside the container, or./data/pastebox.dbon the host, is then used only for migration completion markers. Passwords are stored in hashed form. The admin dashboard shows paste counts, storage usage, policy breakdown, expiring and expired items, and the current paste storage backend. It also lets administrators enable or disable uploads, delete a single paste, or bulk-delete selected pastes. -
Admin Password Reset: If you lose the admin password, set
ADMIN_RESET_TOKENindocker-compose.yml, restart the container, and open/admin/reset. Enter the reset token and a new password. After reset, existing admin sessions are invalidated and you must log in again with the new password. -
Admin Manage Page: Every successful upload also gets a private manage URL using
?manage=.... The manage page lets you copy the public URL and manage URL, switch between public and password-protected access, change the retention policy, and delete the paste. If a password-protected paste is converted back to public, Pastebox first asks for the current generated password. -
Syntax Highlighting Support: Syntax highlighting is supported for common text formats including
.txt,.md,.log,.csv,.conf,.yaml,.toml,.go,.rs,.js,.py,.ts,.php,.html,.css,.sql,.lua, and shell scripts such as.sh.Dockerfile,*.Dockerfile,Makefile,.env.example,.gitignore,compose.yaml,compose.yml,docker-compose.yaml,docker-compose.yml,nginx.conf, and*.nginx.confare also detected by filename. -
Long-line Wrap Mode: When a paste contains a very long single line, the view page shows a
Long line detectedhint and provides aWrapbutton so you can switch from horizontal scrolling to wrapped reading mode in the browser. -
Paste Clone: You can clone the current paste into a new link by clicking the
Clonebutton on the view page. -
Open Graph Metadata: Paste pages expose a
Pastebox - CODEpreview title. Public pastes use the original filename as the preview description when available, while password-protected pastes expose onlyPassword-protected pasteand never reveal the filename, label, content, password, or management tokens.
For details about the data policy header, see DATA_POLICY.md




