Onetime Secret
Onetime Secret shares passwords and other sensitive text through links that work only once
Updated
Onetime Secret is an open source alternative to pasting secrets into email and chat. It is a Ruby application (Rack, Roda and Puma, not Rails) with a Vue frontend built by Vite and pnpm. It stores everything in Redis or Valkey. Upstream documents Docker, Kamal and bare-metal installs; the steps below follow the bare-metal path.
Requirements
Create a Redis database on the app's Databases tab and attach it as REDIS_URL. Onetime Secret keeps all secrets in Redis, so enable persistence and backups for it.
Onetime Secret runs in simple authentication mode by default, which needs nothing else. The full mode adds PostgreSQL and RabbitMQ; RabbitMQ is not something Hatchbox provides, so this guide covers simple mode.
Repository
Fork onetimesecret/onetimesecret, add the script below, and set your fork as the Git URL:
https://github.com/YOUR-USERNAME/onetimesecret.git
Deploy from a release tag such as v0.26.14 rather than main, which moves daily. In your fork, also change .node-version to a full Node 26 release such as 26.10.0. Hatchbox installs exactly the version written there and upstream's file only says 26.
Environment Variables
SECRET=run-openssl-rand-hex-32
HOST=secrets.example.com
SSL=true
RACK_ENV=production
SECRET is the root key every stored secret is encrypted with. Generate it once with openssl rand -hex 32, back it up, and never change it: existing secrets become unreadable without it. HOST is your domain without a scheme. SSL=true makes generated links use HTTPS, which Hatchbox's Caddy terminates.
Outbound email for account verification and notifications is optional:
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USERNAME=user
SMTP_PASSWORD=password
FROM_EMAIL=secure@example.com
Build Scripts
Hatchbox runs bundle install and pnpm install for you. This script builds the frontend and copies the default config files into place, as the upstream Dockerfile does.
.hatchbox/post-build
#!/usr/bin/env bash
set -e
# Record the deployed revision for the frontend build
echo "${REVISION:0:7}" > .commit_hash.txt
# Build the Vue frontend into public/web/dist
pnpm run build
# Seed config files from the shipped defaults
for file in etc/defaults/*.defaults.*; do
target="etc/$(basename "$file" | sed 's/\.defaults//')"
[ -e "$target" ] || cp "$file" "$target"
done
[ -e etc/puma.rb ] || cp etc/examples/puma.example.rb etc/puma.rb
The frontend build needs python3, which Ubuntu includes, for the locale sync step. The Vite build peaks at about 4 GB of memory, so add swap on servers with 2 GB of RAM or less.
Processes
Hatchbox detects Puma on the first deploy and adds a server process. Edit it so it uses upstream's Puma config, which reads PORT from the environment:
bundle exec puma -C etc/puma.rb
Runs on web servers. No worker process is needed in simple mode.
First Login
Create the first admin ("colonel") account over SSH. Change into the app's current directory on the server so the app's environment variables are loaded, then run:
bundle exec bin/ots customers create you@example.com --role colonel
It prints a generated password and the account is verified immediately. Sign in at your domain. The admin console is at /colonel.
Notes
Configuration lives in etc/config.yaml, which the script copies from etc/defaults/config.defaults.yaml. Most settings read environment variables, so you can configure the app from the Environment tab. For settings without an environment variable, commit your own etc/config.yaml to your fork and the script leaves it alone.
To update, merge the new upstream tag into your fork and deploy.