Run a Cashu Mint
The mint. This is the service that issues and redeems ecash using your Lightning node as a backend for deposits and withdrawals. Mints are elegant in their operational simplicity, once you’ve gotten the setup right.
This is the full walkthrough. It uses CDK, the Cashu
Development Kit, whose cdk-mintd is a production-minded mint daemon in Rust. You
install its official prebuilt binary, point it at your LND node, store its data in the
Postgres you set up for Lightning, and expose it on the internet with a Cloudflare Tunnel.
What this step covers
Section titled “What this step covers”cdk-mintdinstalled from its official prebuilt binary, checksum-verified and placed as a system binary.- An isolated
cdk-mintdservice user that reads your LND node’s TLS certificate and macaroon. - Postgres and Redis wired in: Postgres for the mint database (the same instance your LND node uses) and Redis for the response cache.
- A hardened systemd service so the mint starts on boot and restarts on failure.
- A public mint URL served through a Cloudflare Tunnel, with TLS terminated at Cloudflare’s edge.
Requirements
Section titled “Requirements”- A running LND node from the Lightning step,
reachable on its gRPC port (
10009by default), with its TLS certificate and macaroons on this machine. - PostgreSQL installed and running. The Lightning step sets this up if you followed its tip to choose the Postgres backend. You reuse that instance here.
- A domain. Registered with any registrar. The mint is exposed through a Cloudflare Tunnel, which routes a hostname on your domain to it.
- A free Cloudflare account. Add your domain to it and point the domain’s nameservers at Cloudflare, so the tunnel can manage the hostname’s DNS.
Preparations
Section titled “Preparations”Install Redis
Section titled “Install Redis”cdk-mintd caches deterministic responses (NUT-19) so a retried mint, swap, or melt
returns the same result instead of being processed twice. Backing that cache with
Redis keeps it across restarts.
- With user
admin, install Redis and enable it to start on boot
sudo apt updatesudo apt install redis-serversudo systemctl enable redis-server- Confirm Redis is listening on
127.0.0.1only, reachable from this machine and not the network
sudo ss -tulpn | grep 6379Example of expected output:
tcp LISTEN 0 511 127.0.0.1:6379 0.0.0.0:* users:(("redis-server",pid=1234,fd=6))Create the mint database
Section titled “Create the mint database”Reuse the Postgres instance your LND node runs on, but give the mint its own role
instead of the shared admin one. Create a cdk_mintd role and a database it owns.
- With user
admin, create the role and its database, choosing a strong password
sudo -u postgres psql -c "CREATE ROLE cdk_mintd WITH LOGIN PASSWORD 'your-mint-db-password';"sudo -u postgres createdb -O cdk_mintd cdk_mintdInstallation
Section titled “Installation”Create the cdk-mintd user & group
Section titled “Create the cdk-mintd user & group”For improved security, create a dedicated cdk-mintd user to run the mint. A
dedicated user limits the damage if the daemon is ever compromised: an attacker is
confined to this user’s permissions and cannot reach the rest of the machine.
- With user
admin, create thecdk-mintduser and group
sudo adduser --disabled-password --gecos "" cdk-mintd- Add the
cdk-mintduser to thelndgroup, so it can read your Lightning node’s TLS certificate and macaroon
sudo usermod -aG lnd cdk-mintdGive the mint access to your LND credentials
Section titled “Give the mint access to your LND credentials”cdk-mintd authenticates to LND with a TLS certificate and an admin macaroon. As a
member of the lnd group, the cdk-mintd user has read-only access to those files;
symlink the LND data directory into its home so the
daemon finds them under ~/.lnd.
- Change to the
cdk-mintduser
sudo su - cdk-mintd- Link the LND data directory into the
cdk-mintdhome
ln -s /data/lnd /home/cdk-mintd/.lnd- Check that the symbolic link was created correctly
ls -la /home/cdk-mintd/.lndExample of expected output:
lrwxrwxrwx 1 cdk-mintd cdk-mintd 9 Jun 19 12:00 /home/cdk-mintd/.lnd -> /data/lnd- Come back to the
adminuser
exit- Make the LND data directories browsable by the group, and allow the group to read
the
admin.macaroon
sudo chmod -R g+X /data/lnd/data/sudo chmod g+r /data/lnd/data/chain/bitcoin/mainnet/admin.macaroonDownload and install cdk-mintd
Section titled “Download and install cdk-mintd”CDK publishes a static binary for each release, built with the Postgres and Redis features this guide uses, so there is nothing to compile.
- With user
admin, download the release binary for your architecture and its checksum file
cd /tmpVERSION=0.17.1ARCH=$(uname -m)wget https://github.com/cashubtc/cdk/releases/download/v$VERSION/cdk-mintd-$VERSION-$ARCHwget https://github.com/cashubtc/cdk/releases/download/v$VERSION/SHA256SUMS- Verify the download against the checksum published with the release
sha256sum --check --ignore-missing SHA256SUMSExample of expected output:
cdk-mintd-0.17.1-x86_64: OK- Install the binary to a system path, confirm the version, and remove the downloads
sudo install -m 0755 cdk-mintd-$VERSION-$ARCH /usr/local/bin/cdk-mintdcdk-mintd --versionrm cdk-mintd-$VERSION-$ARCH SHA256SUMSExample of expected output:
cdk-mintd 0.17.1Create the data directory
Section titled “Create the data directory”The mint keeps its configuration and seed in a work directory. Following the same
layout, put it under /data.
- With user
admin, create the work directory and give it to thecdk-mintduser
sudo mkdir -p /data/cdk-mintdsudo chown -R cdk-mintd:cdk-mintd /data/cdk-mintdGenerate the mint seed
Section titled “Generate the mint seed”cdk-mintd derives the mint’s signing keys from a standard BIP-39 seed phrase.
The mint signs with it on every operation, so the seed lives here on the server.
CDK does not generate one for you, so make a fresh 24 word phrase with the BIP-39 reference
implementation, which Ubuntu packages.
- With user
admin, install the generator and print a fresh phrase
sudo apt install python3-mnemonicpython3 -c "from mnemonic import Mnemonic; print(Mnemonic('english').generate(strength=256))"Example of expected output (yours will differ, and you must use your own):
army van defense carry jealous true garbage claim echo media make ...- Write the phrase down and keep an offline copy before going further. You paste it
into
config.tomlin the next step, but the server should not be the only place it lives: if the disk dies, that offline copy is half of how you restore the mint.
Configuration
Section titled “Configuration”- Change to the
cdk-mintduser and create the config file in the work directory
sudo su - cdk-mintdnano /data/cdk-mintd/config.toml- Paste the following, replacing the placeholders. Set
urlto the public hostname you will route through the tunnel below, paste your seed intomnemonic, and use the LND macaroon path for your network (mainnetshown). Save and exit
[info]url = "https://mint.yourdomain.com/"listen_host = "127.0.0.1"listen_port = 8085mnemonic = "your twelve or twenty-four word seed phrase here"
[info.http_cache]backend = "redis"ttl = 60tti = 60key_prefix = "mintd"connection_string = "redis://127.0.0.1:6379"
[mint_info]name = "Your Mint"description = "Sovereign bank in cyberspace"
[database]engine = "postgres"
[database.postgres]tls_mode = "disable"
[ln]ln_backend = "lnd"min_mint = 1max_mint = 500000min_melt = 1max_melt = 500000
[lnd]address = "https://localhost:10009"cert_file = "/home/cdk-mintd/.lnd/tls.cert"macaroon_file = "/home/cdk-mintd/.lnd/data/chain/bitcoin/mainnet/admin.macaroon"
[mint_management_rpc]enabled = trueaddress = "127.0.0.1"port = 8086- Come back to the
adminuser
exitCreate systemd service
Section titled “Create systemd service”Now ensure the mint starts as a service so that it is always running, restarts on failure, and comes back after a reboot.
- With user
admin, create the service file
sudo nano /etc/systemd/system/cdk-mintd.service- Paste the following configuration. Save and exit
# Orchard: systemd unit for cdk-mintd# /etc/systemd/system/cdk-mintd.service
[Unit]Description=CDK Cashu mint daemonRequires=lnd.service postgresql.service redis-server.serviceAfter=lnd.service postgresql.service redis-server.service
[Service]ExecStart=/usr/local/bin/cdk-mintd --work-dir /data/cdk-mintd
User=cdk-mintdGroup=cdk-mintd
# Process management####################Restart=on-failureRestartSec=30Type=simple
# Hardening Measures####################PrivateTmp=trueProtectSystem=fullNoNewPrivileges=truePrivateDevices=true
[Install]WantedBy=multi-user.target- Reload systemd so it sees the new unit
sudo systemctl daemon-reload- Enable autoboot (optional)
sudo systemctl enable cdk-mintd- Prepare
cdk-mintdmonitoring by the systemd journal. You can exit monitoring at any time withCtrl-C
journalctl -fu cdk-mintdTo keep an eye on the mint as it starts, open a second terminal, connect to the node,
and log in as admin.
- With user
admin, start the service
sudo systemctl start cdk-mintdWatch the journal in the other terminal until it settles.
Validation
Section titled “Validation”- Ensure the mint is listening on its local
8085port
sudo ss -tulpn | grep 8085Example of expected output:
tcp LISTEN 0 1024 127.0.0.1:8085 0.0.0.0:* users:(("cdk-mintd",pid=5678,fd=10))- Ask the mint for its public info from the machine itself. It returns a JSON document describing your mint
curl http://127.0.0.1:8085/v1/infoExample of expected output (truncated):
{"name":"Your Mint","version":"cdk-mintd/0.17.1","description":"Sovereign bank in cyberspace","contact":[...],"nuts":{...}}Put your mint online with a Cloudflare Tunnel
Section titled “Put your mint online with a Cloudflare Tunnel”Wallets reach a mint over HTTPS, so the mint needs a public hostname. A Cloudflare Tunnel makes one outbound connection from this machine to Cloudflare’s network and forwards traffic to the mint’s local port — your home IP is never exposed, and there is no router port to forward.
Follow MiniBolt’s
Cloudflare Tunnel guide
to install cloudflared, authenticate it to your Cloudflare account, create a tunnel,
and run it as a systemd service. It is the same cloudflared setup MiniBolt uses for
its other services, with checksum-verified downloads and screenshots for the Cloudflare
dashboard steps. Two things are specific to the mint:
- Use your mint’s hostname throughout. Wherever the guide uses
subdomain.domain.com— in thecloudflared tunnel route dnscommand and in the tunnel config — use the same hostname you set asurlinconfig.toml, for examplemint.yourdomain.com. - Route it to the mint’s local port. In the tunnel’s
config.yml, replace the guide’s example ingress rules with a single rule for the mint, keeping thehttp_status:404rule last:
ingress: # Cashu mint - hostname: mint.yourdomain.com service: http://localhost:8085 - service: http_status:404With the tunnel running, ask your public mint for its info from any machine. You should get the same JSON as the local check, now over HTTPS:
curl https://mint.yourdomain.com/v1/infoExtras (optional)
Section titled “Extras (optional)”- Mint information.
name,description,contact_email,motd, andicon_urlin[mint_info]are what wallets show before someone trusts your mint. Keep them accurate. Once Orchard is connected you manage these from the dashboard; see Mint information. - Fees and limits. Set
input_fee_ppkunder[info]to charge a per-input fee,fee_percentandreserve_fee_minunder[lnd]to tune the Lightning fee reserve, andmax_inputs/max_outputsunder[limits]to cap transaction size. Once Orchard is connected, you can change the input fee by rotating to a new keyset; see Keysets. - A least-privilege macaroon. Instead of
admin.macaroon, bake an LND macaroon limited to invoice and offchain permissions and pointmacaroon_fileat it, so a leaked mint credential cannot touch the rest of your node. - Other ways to expose the mint. The Tunnel is the easy option and hides your IP.
Alternatives: a VPS relay you control (also hides your IP, no Cloudflare in the path),
or a forwarded port from home (simplest, but exposes your IP). Tor won’t work as wallets
can’t reach a
.onionmint.
Secure the management API with mutual TLS
Section titled “Secure the management API with mutual TLS”The management gRPC runs insecurely in the main setup because Orchard shares this machine
and the port is reachable only from the machine itself. If Orchard runs on a different host, or you want
authentication once the connection leaves this machine, give the interface mutual TLS instead.
cdk-mintd does not generate these certificates: you create a small private CA that
signs one certificate for the mint and one for Orchard.
- As the
cdk-mintduser, create the TLS directory the mint reads and generate the certificates
sudo su - cdk-mintdmkdir -p /data/cdk-mintd/tls && cd /data/cdk-mintd/tls
# A private CA that signs both the mint (server) and Orchard (client)openssl genpkey -algorithm RSA -out ca.keyopenssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -subj "/CN=cdk-mintd CA" -out ca.pem
# The mint's server certificate, valid for the local address Orchard dialsopenssl genpkey -algorithm RSA -out server.keyopenssl req -new -key server.key -subj "/CN=cdk-mintd" -out server.csropenssl x509 -req -in server.csr -CA ca.pem -CAkey ca.key -CAcreateserial -days 3650 -sha256 \ -extfile <(printf "subjectAltName=IP:127.0.0.1,DNS:localhost") -out server.pem
# Orchard's client certificate, signed by the same CAopenssl genpkey -algorithm RSA -out client.keyopenssl req -new -key client.key -subj "/CN=orchard" -out client.csropenssl x509 -req -in client.csr -CA ca.pem -CAkey ca.key -CAcreateserial -days 3650 -sha256 -out client.pem
rm server.csr client.csrexitThe mint reads server.pem, server.key, and ca.pem from this directory. Because the
directory now exists with certificates in it, cdk-mintd serves the management gRPC over
mutual TLS on its next start, and any client must present a certificate signed by your CA.
- With user
admin, restart the mint to pick up the certificates
sudo systemctl restart cdk-mintdGive Orchard the three client files so it can connect: ca.pem to verify the mint, plus
client.pem and client.key as its own identity. To return to running insecurely, stop
the mint, remove /data/cdk-mintd/tls, and start it again.
Upgrade
Section titled “Upgrade”- With user
admin, stop the service
sudo systemctl stop cdk-mintd-
Back up the mint database before upgrading, so you can roll back if the new version misbehaves. Orchard reads the database straight from Postgres, so the backup works with the mint stopped — and stopping first gives a clean snapshot. See Backup and restore.
-
Download the new release binary and its checksum for your architecture (replace the version with the release you want)
cd /tmpVERSION=0.17.1ARCH=$(uname -m)wget https://github.com/cashubtc/cdk/releases/download/v$VERSION/cdk-mintd-$VERSION-$ARCHwget https://github.com/cashubtc/cdk/releases/download/v$VERSION/SHA256SUMSsha256sum --check --ignore-missing SHA256SUMS- Replace the binary, clean up, and start the service again
sudo install -m 0755 cdk-mintd-$VERSION-$ARCH /usr/local/bin/cdk-mintdrm cdk-mintd-$VERSION-$ARCH SHA256SUMSsudo systemctl start cdk-mintdUninstall
Section titled “Uninstall”Uninstall service
Section titled “Uninstall service”- With user
admin, stop and disable the service, then remove the unit
sudo systemctl stop cdk-mintdsudo systemctl disable cdk-mintdsudo rm /etc/systemd/system/cdk-mintd.servicesudo systemctl daemon-reloadDelete user & group
Section titled “Delete user & group”- Delete the
cdk-mintduser. Do not worry about theuserdel: cdk-mintd mail spool (/var/mail/cdk-mintd) not foundmessage
sudo userdel -rf cdk-mintdDrop the database
Section titled “Drop the database”- Drop the mint database
sudo -u postgres dropdb cdk_mintdRemove the binary and data
Section titled “Remove the binary and data”- Remove the work directory and the installed binary
sudo rm -rf /data/cdk-mintdsudo rm /usr/local/bin/cdk-mintdRemove the public route
Section titled “Remove the public route”- Delete the mint’s ingress entry from
/home/admin/.cloudflared/config.yml, remove the DNS record from your Cloudflare dashboard, and delete the tunnel if it is no longer used. Use the name you gave it (cloudflared tunnel listshows it)
cloudflared tunnel delete <NAME>Port reference
Section titled “Port reference”| Port | Protocol | Use |
|---|---|---|
| 8085 | TCP (localhost) | Mint HTTP API, forwarded by the tunnel |
| 8086 | TCP (localhost) | Management gRPC, used by Orchard |
| 6379 | TCP (localhost) | Redis response cache |
| 5432 | TCP (localhost) | Postgres, shared with LND |
The mint has no inbound public port. Wallets reach it through the Cloudflare Tunnel’s outbound connection.
New Mint Cashu Mint
Last updated: