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). Ensure jq is installed (curl is usually available).
export AKASH_META_URL="${AKASH_META_URL:-https://raw.githubusercontent.com/akash-network/net/main/mainnet/meta.json}"export AKASH_CHAIN_ID="$(curl -sSf "$AKASH_META_URL" | jq -r '.chain_id')"export AKASH_NODE="$(curl -sSf "$AKASH_META_URL" | jq -r '.apis.rpc[0].address')"so 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