# Onetime Secret

Onetime Secret shares passwords and other sensitive text through links that work only once

[Onetime Secret](https://github.com/onetimesecret/onetimesecret) 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&#39;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](https://github.com/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&#39;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&#39;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 &quot;${REVISION:0:7}&quot; &gt; .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=&quot;etc/$(basename &quot;$file&quot; | sed &#39;s/\.defaults//&#39;)&quot;
  [ -e &quot;$target&quot; ] || cp &quot;$file&quot; &quot;$target&quot;
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&#39;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 (&quot;colonel&quot;) account over SSH. Change into the app&#39;s `current` directory on the server so the app&#39;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.
