Deploy an Akash validator on the network with Tendermint Key Management System (TMKMS) for maximum security. Your validator private key is stored on a separate, secured server instead of the validator node itself.
Time: 2-3 hours
Overview
This guide shows how to deploy a validator with:
Features:
- Remote Key Management - Validator key stored on separate TMKMS server
- Encrypted Communication - Stunnel provides TLS encryption between validator and TMKMS
- Runs on Akash - Validator deployed on Akash Network
- State Sync - Fast initial synchronization (~5 minutes)
Security Benefits:
- Validator private key never on validator node
- Key can be on HSM (Hardware Security Module)
- Key isolated from internet-facing validator
- Additional layer against key theft
Architecture
+-----------------------+ Encrypted +-----------------------+| Akash Validator | (Stunnel) | TMKMS Server || (Deployment) | <--------------------> | (Your Server) || | | || +----------------+ | | +----------------+ || | Validator Node | | Port 26658 | | TMKMS | || | (no priv key) |---|-----(signs blocks)------|--| (has priv key) | || +----------------+ | | +----------------+ || | | || +----------------+ | | +----------------+ || | Stunnel | | Port 36658 | | Stunnel | || | Server |---|-----(TLS tunnel)--------|--| Client (Docker)| || +----------------+ | | +----------------+ |+-----------------------+ +-----------------------+ | | | | v v Akash Network Your InfrastructureHow it works:
- Validator receives block to sign
- Sends signing request to Stunnel server (port 26658)
- Stunnel server encrypts and forwards to Stunnel client (port 36658)
- Stunnel client decrypts and forwards to TMKMS (port 26658)
- TMKMS signs with private key
- Signature returns via same encrypted path
- Validator broadcasts signed block
Prerequisites
Before starting, ensure you have:
1. Akash Wallet
- Funded with 50+ AKT for deployment deposits
- See Akash CLI Installation
2. Separate TMKMS Server
- OS: Ubuntu 20.04+ or 22.04 LTS
- CPU: 2 cores minimum
- RAM: 2 GB minimum
- Network: Internet connectivity
- Security: Firewall configured, SSH hardened
- Access: Root or sudo access
3. Validator Private Key
- From existing validator, or
- Generate new key (we’ll cover this)
4. Akash Console
- Familiar with deploying on Akash
- See Akash Console Guide
Step 1 - Obtain Validator Private Key
You need a validator private key to import into TMKMS.
Option A: Use Existing Validator Key
If you have an existing validator:
cat ~/.akash/config/priv_validator_key.jsonCopy this file’s contents and save securely.
Option B: Generate New Key
Deploy a temporary Akash node to generate a key:
- Follow Node Build via Omnibus
- Once deployed, run in shell:
cat ~/.akash/config/priv_validator_key.json- Copy the key contents
- Close the deployment
Key Format
Your private key file should look like:
{ "address": "134FCAC9E5C...", "pub_key": { "type": "tendermint/PubKeyEd25519", "value": "BrL0wA8DWiVvm..." }, "priv_key": { "type": "tendermint/PrivKeyEd25519", "value": "3RphlkX7PucBKSdhFKviFV5TI..." }}Security: Save this file securely offline. You’ll import it to TMKMS server next.
Step 2 - Deploy Validator on Akash
Deploy the validator node (without private key) on Akash Network with Stunnel server.
Get SDL Template
Template: cosmos-omnibus TMKMS example
Customize SDL
The SDL contains 2 services:
- node - Validator node (private key managed by TMKMS)
- proxy - Stunnel server (encrypts communication)
Required customizations:
services: node: env: - MONIKER=my-validator # Change this
proxy: env: - PSK=<your-unique-psk> # Generate strong PSKGenerate PSK (Pre-Shared Key):
openssl rand -hex 32Save this PSK - you’ll need it for Stunnel client configuration.
Full SDL (Customized)
---version: "2.0"
services: node: image: ghcr.io/akash-network/cosmos-omnibus:v1.2.35-akash-v1.1.0 env: - MONIKER=my-validator # Change this - CHAIN_JSON=https://raw.githubusercontent.com/akash-network/net/main/mainnet/meta.json - MINIMUM_GAS_PRICES=0.025uakt - P2P_POLKACHU=1 - STATESYNC_POLKACHU=1 - AKASH_PRIV_VALIDATOR_LADDR=tcp://0.0.0.0:26658 # Remote signer expose: - port: 26657 # RPC (internal to proxy only) to: - service: proxy - port: 26658 # Signing port (internal to proxy only) to: - service: proxy params: storage: data: mount: /root/.akash
proxy: image: ghcr.io/ovrclk/stunnel-proxy:v0.0.1 env: - PSK=<your-psk-from-above> # Must match Stunnel client - STUNNEL_SVC_RPC_ACCEPT=36657 - STUNNEL_SVC_RPC_CONNECT=node:26657 - STUNNEL_SVC_SIGNER_ACCEPT=36658 - STUNNEL_SVC_SIGNER_CONNECT=node:26658 expose: - port: 36657 # Encrypted RPC (global) to: - global: true - port: 36658 # Encrypted signer (global) to: - global: true
profiles: compute: node: resources: cpu: units: 8 memory: size: 16Gi storage: - size: 1Gi - name: data size: 500Gi attributes: persistent: true class: beta3 proxy: resources: cpu: units: 1 memory: size: 512Mi storage: size: 512Mi
placement: dcloud: attributes: host: akash signedBy: anyOf: - akash1365yvmc4s7awdyj3n2sav7xfx76adc6dnmlx63 pricing: node: denom: uakt amount: 100000 proxy: denom: uakt amount: 10000
deployment: node: dcloud: profile: node count: 1 proxy: dcloud: profile: proxy count: 1Deploy via Akash Console
- Open Akash Console
- Click “Deploy”
- Select “Empty” template
- Paste your customized SDL
- Accept deposit (increase to 50+ AKT for longer runtime)
- Select trusted provider (validator security is critical!)
- Submit deployment
Wait for Deployment
Expected behavior:
- Deployment will show as running
- Node container will restart repeatedly with error
- This is normal! Node is waiting for TMKMS connection
Capture Connection Details
You need 3 pieces of information from the deployment:
- Go to “LEASES” tab
- Find the “Forwarded Ports” section
- Note:
Provider URI: (e.g., provider.mainnet-1.ca.aksh.pw)
Port for 36658 → ???: (e.g., 31684) - This is your Signer Port
Port for 36657 → ???: (e.g., 32675) - This is your RPC Port
Save these - you’ll need them for Stunnel client configuration.
Step 3 - Setup TMKMS Server
All commands in this step run on your TMKMS server (not the Akash deployment).
Install Dependencies
# Update systemsudo apt updatesudo apt install -y git build-essential ufw curl jq libusb-1.0-0-dev
# Install Rustcurl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shsource $HOME/.cargo/env
# Set Rust flags for optimizationexport RUSTFLAGS=-Ctarget-feature=+aes,+ssse3Install TMKMS
cd ~git clone https://github.com/iqlusioninc/tmkms.gitcd ~/tmkmscargo install tmkms --features=softsignThis takes 5-10 minutes to compile.
Initialize TMKMS
mkdir -p /etc/tmkmstmkms init /etc/tmkms/Created files:
/etc/tmkms/tmkms.toml- Configuration file/etc/tmkms/secrets/- Directory for keys
Import Validator Private Key
Create the key file:
vi /etc/tmkms/secrets/priv_validator_key.jsonPaste your validator private key from Step 1:
{ "address": "134FCAC9E5C...", "pub_key": { "type": "tendermint/PubKeyEd25519", "value": "BrL0wA8DWiVvm..." }, "priv_key": { "type": "tendermint/PrivKeyEd25519", "value": "3RphlkX7PucBKSdhFKviFV5TI..." }}Convert Key to TMKMS Format
tmkms softsign import \ /etc/tmkms/secrets/priv_validator_key.json \ /etc/tmkms/secrets/priv_validator_key.softsignExpected output:
Imported validator key akashvalcons1...Secure Key Permissions
chmod 600 /etc/tmkms/secrets/priv_validator_key.softsignDelete Original Key File
shred -uvz /etc/tmkms/secrets/priv_validator_key.jsonImportant: The key is now in .softsign format. Back up this file securely offline.
Configure TMKMS
Create configuration file:
cat > /etc/tmkms/tmkms.toml <<EOF# Tendermint KMS configuration
[[chain]]id = "akashnet-2"key_format = { type = "bech32", account_key_prefix = "akashpub", consensus_key_prefix = "akashvalconspub" }state_file = "/etc/tmkms/state/akashnet-2-consensus.json"
[[providers.softsign]]chain_ids = ["akashnet-2"]key_type = "consensus"path = "/etc/tmkms/secrets/priv_validator_key.softsign"
[[validator]]chain_id = "akashnet-2"addr = "tcp://127.0.0.1:36658"secret_key = "/etc/tmkms/secrets/kms-identity.key"protocol_version = "v0.34"reconnect = trueEOFImportant settings:
chain_id- Must beakashnet-2(mainnet)addr- Points to Stunnel client (local port 36658)reconnect- Automatically reconnects if connection drops
Create State Directory
mkdir -p /etc/tmkms/stateStep 4 - Setup Stunnel Client
The Stunnel client provides encrypted connection between TMKMS and your Akash validator.
Run on TMKMS server (same server as TMKMS).
Install Docker and Docker Compose
# Install Dockercurl -fsSL https://get.docker.com -o get-docker.shsh get-docker.sh
# Install Docker Composesudo curl -L "https://github.com/docker/compose/releases/download/v2.24.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-composesudo chmod +x /usr/local/bin/docker-composeClone Stunnel Proxy Repository
mkdir -p ~/stunnelcd ~/stunnelgit clone https://github.com/akash-network/stunnel-proxycd stunnel-proxy/clientConfigure Stunnel Client
Edit docker-compose.yml:
vi docker-compose.ymlUpdate these variables with your deployment details from Step 2:
environment: - PSK=<your-psk> # Must match SDL PSK - STUNNEL_SVC_RPC_CONNECT=<provider-uri>:<rpc-port> - STUNNEL_SVC_SIGNER_CONNECT=<provider-uri>:<signer-port>Example:
environment: - PSK=a1b2c3d4e5f6789... - STUNNEL_SVC_RPC_CONNECT=provider.mainnet-1.ca.aksh.pw:32675 - STUNNEL_SVC_SIGNER_CONNECT=provider.mainnet-1.ca.aksh.pw:31684Start Stunnel Client
docker-compose up -dVerify Stunnel Client
docker-compose logs -fExpected: Should show successful TLS connection to Stunnel server.
Step 5 - Start TMKMS
Run TMKMS service on your TMKMS server.
Start in Foreground (Testing)
tmkms start -c /etc/tmkms/tmkms.tomlExpected Initial Logs
While waiting for Stunnel connection, you’ll see:
INFO tmkms::commands::start: tmkms starting...INFO tmkms::keyring: [keyring:softsign] added consensus Ed25519 keyINFO tmkms::connection::tcp: KMS node ID: 948f8fee83f7715f99b8b8a53d746ef00e7b0d9eERROR tmkms::client: [akashnet-2@tcp://127.0.0.1:36658] I/O error: Connection refused (os error 111)This is normal! TMKMS is trying to connect to Stunnel client (port 36658).
Wait for Connection
After Stunnel client connects (from Step 4), you’ll see:
INFO tmkms::connection::tcp: KMS node ID: 7a1f7c7f726d94787045cca9fee05c1ec67cd09aINFO tmkms::session: [akashnet-2@tcp://127.0.0.1:36658] connected to validator successfullyWARN tmkms::session: [akashnet-2@tcp://127.0.0.1:36658]: unverified validator peer ID!Success! TMKMS is now connected and signing blocks.
Run as Service (Production)
Create systemd service:
sudo tee /etc/systemd/system/tmkms.service > /dev/null <<EOF[Unit]Description=Akash TMKMSAfter=network.target
[Service]Type=simpleUser=rootExecStart=/root/.cargo/bin/tmkms start -c /etc/tmkms/tmkms.tomlRestart=on-failureRestartSec=3LimitNOFILE=65535
[Install]WantedBy=multi-user.targetEOFEnable and start:
sudo systemctl daemon-reloadsudo systemctl enable tmkmssudo systemctl start tmkmsCheck status:
sudo systemctl status tmkmssudo journalctl -u tmkms -fStep 6 - Verify Everything is Working
Check TMKMS Logs
sudo journalctl -u tmkms -f --lines 50Look for:
- **
connected to validator successfully - **Signing messages (height increasing)
Check Akash Validator Logs
In Akash Console:
- Go to your deployment
- Click “LOGS” tab
- Select “node” service
Look for:
- **
executed block height=... - **
committed state height=... - **Height increasing continuously
Check Stunnel Proxy Logs
In Akash Console:
- Select “proxy” service from logs
- Look for: TLS connection success messages
Example successful logs:
LOG5: Service [signer] connected remote serverLOG6: TLS accepted: previous session reusedLOG6: TLSv1.3 ciphersuite: TLS_CHACHA20_POLY1305_SHA256Step 7 - Create Your Validator
Now that your node is running with TMKMS, create the validator.
Set Environment Variables
On your local machine (with akash CLI):
export AKASH_NET="https://raw.githubusercontent.com/akash-network/net/main/mainnet"export AKASH_CHAIN_ID="$(curl -s "$AKASH_NET/chain-id.txt")"export AKASH_NODE="$(curl -s "$AKASH_NET/rpc-nodes.txt" | head -1)"export AKASH_KEYNAME=<your-key-name>export AKASH_GAS=autoexport AKASH_GAS_ADJUSTMENT=1.25export AKASH_GAS_PRICES=0.025uaktGet Validator Public Key
You need the validator’s consensus public key. This is in the TMKMS startup logs.
Look for:
INFO tmkms::keyring: added consensus Ed25519 key: akashvalconspub1zcjduepq...Or query your deployment via shell and run:
akash tendermint show-validatorSave this value as AKASH_VALOPER_PUBKEY.
Create Validator Transaction
akash tx staking create-validator \ --amount=1000000uakt \ --pubkey="$AKASH_VALOPER_PUBKEY" \ --moniker="<your-moniker>" \ --chain-id="$AKASH_CHAIN_ID" \ --commission-rate="0.10" \ --commission-max-rate="0.20" \ --commission-max-change-rate="0.01" \ --min-self-delegation="1" \ --gas="$AKASH_GAS" \ --gas-adjustment="$AKASH_GAS_ADJUSTMENT" \ --gas-prices="$AKASH_GAS_PRICES" \ --from="$AKASH_KEYNAME"Parameters:
--amount- Self-delegation amount (e.g., 1000000uakt = 1 AKT)--moniker- Your validator’s display name--commission-rate- Starting commission (e.g., 0.10 = 10%)
Verify Validator
akash query staking validator $(akash keys show $AKASH_KEYNAME --bech val -a)Check on Akash Block Explorers:
Security Best Practices
TMKMS Server Hardening
Firewall rules:
sudo ufw default deny incomingsudo ufw default allow outgoingsudo ufw allow 22/tcp # SSHsudo ufw enableDisable password authentication:
sudo vi /etc/ssh/sshd_config# Set: PasswordAuthentication nosudo systemctl restart sshdKey Backups
Critical files to backup securely offline:
/etc/tmkms/secrets/priv_validator_key.softsign/etc/tmkms/secrets/kms-identity.key- Your wallet mnemonic
Monitoring
Monitor these continuously:
- TMKMS service status
- Stunnel connection
- Validator uptime
- Disk space on validator deployment
High Availability
For production validators:
- Use HSM for TMKMS keys
- Implement Sentry Node Architecture
- Deploy multiple sentries across providers
- Monitor with alerts (PagerDuty, etc.)
Troubleshooting
TMKMS Connection Refused
Symptom: Connection refused (os error 111)
Solutions:
- Verify Stunnel client is running:
docker ps - Check Stunnel client logs:
docker-compose logs -f - Verify PSK matches in both SDL and docker-compose.yml
- Verify ports in tmkms.toml (36658)
Validator Not Signing
Symptom: TMKMS connected but no signing messages
Solutions:
- Check validator logs for errors
- Verify
priv_validator_key.jsondeleted from validator - Restart validator deployment
- Check state file:
cat /etc/tmkms/state/akashnet-2-consensus.json
Stunnel TLS Errors
Symptom: TLS handshake failed
Solutions:
- Verify PSK matches exactly
- Check provider URI and ports
- Verify Akash deployment is running
- Check firewall rules