ON

One-Time Secret

Share sensitive credentials with self-destructing links

Password Managers ★ 2.9k stars Easy setup MIT

One-Time Secret is an open-source service for sharing passwords and other sensitive information through links that can only be viewed once. After the secret is read or expires, it is permanently deleted.

Key features

  • Self-destructing secret links
  • Optional passphrase protection
  • Configurable expiration times
  • Simple REST API

Pros & cons

Strengths

  • Great for one-off credential sharing
  • Easy to self-host

Trade-offs

  • Not a long-term vault

One-Time Secret replaces

Last reviewed Sep 13, 2026 · 825 words

Self-hosting One-Time Secret is a Ruby container and a Redis container, and it works in 10 minutes. The one setting that matters is SECRET: it is the key every stored secret is encrypted with, and if you rotate it, lose it, or let Docker regenerate it, every link in flight becomes garbage. Set it once, back it up, and the rest of this service is pleasantly boring.

What it is for, and what it is not

One-Time Secret does one thing: you paste a password, a token or a paragraph, it gives you a link, and the first person to open the link sees the content while the server deletes it. Optional passphrase, configurable lifetime, done. It is the right tool for sending a database password to a freelancer, a Wi-Fi key to a guest, or an API token to a colleague on a chat platform that keeps logs forever.

It is not a vault. The catalogue's only listed con is "not a long-term vault", and that is exactly right: nothing is meant to survive more than a few days, there is no search, no folders, no browser extension. If you are looking for somewhere to keep passwords, that is Vaultwarden territory, and the password managers category has the full field.

Compose: Redis is the state, SECRET is the key

Redis holds every secret, its metadata and the expiry countdown. The web container is stateless apart from configuration. A working compose looks like this:

services:
  onetimesecret:
    image: onetimesecret/onetimesecret:latest
    environment:
      - HOST=secrets.example.com
      - SSL=true
      - SECRET=replace-with-a-long-random-string
      - REDIS_URL=redis://redis:6379/0
      - [email protected]
    ports:
      - "3000:3000"
    depends_on:
      - redis
    restart: unless-stopped
  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes
    volumes:
      - ./redis-data:/data
    restart: unless-stopped

Three notes. HOST must be the public hostname or generated links point at the wrong place. COLONEL is the admin account address. And enable Redis persistence (the --appendonly yes above) or a Redis restart empties the store and every outstanding link 404s. The official installation docs cover the remaining environment variables, and there are few of them.

Put Caddy or another TLS terminator in front. A secret-sharing service over plain HTTP defeats its own purpose, and SSL=true tells the app to generate https:// links.

Lifetimes and passphrases are the whole security model

Two settings decide how safe a link is. The lifetime (TTL) defaults to hours and can go to days; shorter is safer, because an unread link is a secret sitting on your server waiting for whoever finds the URL. The passphrase adds a second factor delivered through a different channel: link over email, passphrase over chat or voice. For anything you would genuinely mind leaking, use both, and keep the TTL under a day.

The server never learns a passphrase-protected secret's plaintext without the passphrase, but the operator can still see that a secret exists and when it was created. Treat that as the honest limit of the design.

The API is the reason to self-host it

The hosted onetimesecret.com is free for casual use and fine for most people. The reason to run your own is automation: a REST endpoint you can hit from scripts, CI jobs and onboarding tooling without third-party terms of service in the loop.

curl -s -X POST https://secrets.example.com/api/v1/share \
  -d "secret=the-db-password" \
  -d "ttl=3600" \
  -d "passphrase=spoken-over-the-phone"

The response includes a secret_key you turn into a link, and a metadata_key you keep to check whether it has been viewed. Wrap that in a 10-line shell function and "send someone a credential" stops involving chat history at all.

Bitwarden Send may already cover you

If you already run Vaultwarden, its Send feature does burn-after-reading text and file sharing with expiry and passwords, inside apps you already have. For a household or a small team, that is enough and one fewer container. One-Time Secret earns its place when you want the API, a standalone URL you can hand to non-Bitwarden users, or the multi-user account model with per-user API keys. Among direct rivals, PrivateBin is the pastebin-style choice with client-side encryption and no Redis, and Yopass is the smaller Go equivalent.

What I'd do

Deploy it exactly as above behind Caddy, generate SECRET with openssl rand -hex 32, store that string in your password manager before the first container start, and set Redis to append-only. Default TTL of 24 hours, passphrases for anything that touches production. If you already have Vaultwarden and no automation needs, use Send and skip this entirely; if you have a team that hands credentials around in chat, this is the cheapest fix that actually changes behaviour.

Compare One-Time Secret

22 head-to-head comparisons.

Similar password managers apps