Skip to content
CCPEDIAby Unity Nodes
Documentation/Canton Network Docs/Wallet GatewaySet up & operateView on Canton Network Docs ↗

Deploy

For anything beyond local development, run the Wallet Gateway as a container. It ships in both Docker and Helm variants, and the images and charts are public on the GitHub Container Registry, so no access request is required.
  • Docker registry: ghcr.io/digital-asset/wallet-gateway/docker/wallet-gateway:<VERSION>
  • Helm repository: ghcr.io/digital-asset/wallet-gateway/helm/wallet-gateway:<VERSION>
Replace <VERSION> with the version you want to deploy; there is currently no latest tag. To find a version, check the GHCR tags or the matching npm package.

Before you deploy

Make production choices in your configuration file before packaging it. See Configure the Wallet Gateway and the Configuration reference.
  • Use a persistent store. Prefer postgres. The memory store loses all data when a container or pod is recreated, and sqlite persists only if its file is on a persistent volume.
  • Set kernel.publicUrl. Behind a reverse proxy or load balancer, set it to the external URL so OAuth redirects and discovery work.
  • Restrict CORS. Set server.allowedOrigins to your known dApp origins instead of "*".
  • Keep secrets out of the image. Supply clientSecret, adminAuth, and provider API keys through environment variables or a secret manager, not the baked-in config file. See Secrets and environments.

Expose the service

The Wallet Gateway must be reachable over HTTPS from browsers that open the User UI and from hosted dApps that call the dApp API. In Kubernetes, expose it with an Ingress or LoadBalancer that terminates TLS and forwards to the pod port (default 3030). Set kernel.publicUrl to that external URL so OAuth redirects and discovery work correctly. Subpath routing (for example https://wallet.example.com/subpath) is supported.

Docker

The container needs a configuration file. If you don’t have one, generate a sample to start from:
# via Docker
docker run --rm \
  ghcr.io/digital-asset/wallet-gateway/docker/wallet-gateway:<VERSION> --config-example > config.json

# or via npx
npx @canton-network/wallet-gateway-remote@<VERSION> --config-example > config.json
Mount the config and start the service:
docker run -p 3030:3030 \
  -v ${PWD}/config.json:/app/config.json:ro \
  ghcr.io/digital-asset/wallet-gateway/docker/wallet-gateway:<VERSION>
The login page is then available at http://localhost:3030.
If you use the internal signing provider, its signingStore holds private keys. Put it on durable, access-controlled storage, or use a participant node or external custody provider instead. See Signing providers.

Helm

An official Helm chart is available for Kubernetes. The full values schema is published, but the key point is that the Wallet Gateway is configured through the top-level config: key in values.yaml, specified as YAML but using the same schema as config.json.

Signing chart values (signing: {})

The chart’s signing block configures optional external signing drivers (Blockdaemon, DFNS, Fireblocks). Leaving it empty is the common case for participant-based signing:
signing: {}
You do not need participant-specific fields under signing when the participant node handles keys. Add entries only to enable an external custody provider:
signing:
    # optional, define to enable blockdaemon integration -- or omit
    blockdaemon:
        apiUrl: 'http://localhost:5080/api/cwp/canton'
        apiKeyRef:
            name: 'blockdaemon-creds'
            key: 'api-key'
    # optional, define to enable fireblocks integration -- or omit
    fireblocks:
        apiKeyRef:
            name: 'fireblocks-creds'
            key: 'fb-api-key'
        secretRef:
            name: 'fireblocks-creds'
            key: 'fb-secret'

OAuth secrets from Kubernetes secrets (oauthSecrets)

The chart can inject OAuth client secrets from Kubernetes secrets by mapping an environment variable name to a secret reference, then referencing that variable from a network’s auth config:
oauthSecrets:
    # map a kubernetes secret to a Wallet Gateway network auth config
    MY_OAUTH2_CLIENT_SECRET:
        secretRef:
            name: 'my-oauth'
            key: 'client-secret'

config:
    networks:
        - id: 'my-network'
          adminAuth:
              # should correlate to a secret provided in oauthSecrets
              clientSecretEnv: 'MY_OAUTH2_CLIENT_SECRET'

Production configuration highlights

Read the Configuration reference first for the complete breakdown. The following (incomplete) YAML highlights fields worth setting for a production deployment:
kernel:
    # Publicly accessible URL users connect to. Subpath routing is supported.
    publicUrl: 'https://wallet.example.com/subpath'
server:
    # In Helm/k8s, keep the default 3030 and route internally from your
    # Ingress/LoadBalancer (exposed on 443) to the pod's port. Terminate TLS at the cluster edge.
    port: 3030
    # Origins for the set of web dApps allowed to call the dApp API.
    allowedOrigins:
        - 'https://dapp1.example.com'
        - 'https://dapp2.example.com'
    # Default (5mb) may need bumping for large contract payloads.
    requestSizeLimit: '5mb'
    # Default 10000 requests / minute / IP. Bump if you hit HTTP 429 during regular use.
    requestRateLimit: 10000
bootstrap:
    networks:
        - adminAuth:
              # For production, inject OAuth secrets via the environment (secrets manager),
              # naming the environment variable to read for this network.
              clientSecretEnv: 'OAUTH2_CLIENT_SECRET'

Signing provider environment variables

Besides the dynamic clientSecretEnv variables in config, external signing providers read a few static environment variables:
ProviderVariableDescription
FireblocksFIREBLOCKS_API_KEYAPI key for the Fireblocks integration.
FireblocksFIREBLOCKS_SECRETSecret for the Fireblocks integration.
BlockdaemonBLOCKDAEMON_API_KEYAPI key for the Blockdaemon integration.
BlockdaemonBLOCKDAEMON_API_URLURL for the Blockdaemon API.
See Signing providers for more.

Database persistence

SQLite

The default config uses sqlite, which is fine for evaluation and short-lived environments, but PostgreSQL is recommended for production. SQLite stores data in local files; without a persistent volume, all sessions and wallet state are lost when the pod is recreated. Point the stores at a path inside the container:
# config YAML for Helm, or equivalent config.json
signingStore:
    connection:
        type: 'sqlite'
        database: '/data/signing_store.sqlite'
store:
    connection:
        type: 'sqlite'
        database: '/data/store.sqlite'
Then mount a volume for that path:
docker run -p 3030:3030 \
  -v ${PWD}/config.json:/app/config.json:ro \
  -v ${PWD}/data:/data \
  ghcr.io/digital-asset/wallet-gateway/docker/wallet-gateway:<VERSION>

PostgreSQL

Point the store at a PostgreSQL instance:
# config YAML for Helm, or equivalent config.json
store:
    connection:
        type: 'postgres'
        host: '<HOST_NAME>'
        port: 5432
        database: '<DB_NAME>'
        user: '<DB_USERNAME>'
        password: '<DB_PASSWORD>'
For TLS to PostgreSQL, add an ssl block (passed through to the Node.js pg driver). See PostgreSQL over TLS/SSL.

Local PostgreSQL over TLS (Docker)

For local development you can run PostgreSQL in Docker with TLS enabled and point the Wallet Gateway’s store / signingStore at it.
This configures TLS between the Wallet Gateway and PostgreSQL. It does not configure HTTPS for browsers. For browser/client HTTPS, terminate TLS in your reverse proxy or ingress and set kernel.publicUrl to the external https://... URL.
1. Create TLS files for Postgres (self-signed):
mkdir -p .dev/postgres-tls
cd .dev/postgres-tls

# server key + cert (CN=localhost)
openssl req -x509 -newkey rsa:2048 -nodes \
  -keyout server.key -out server.crt -days 365 \
  -subj "/CN=localhost"

# Postgres requires strict key perms
chmod 600 server.key

# pg_hba: allow local socket (for init), enforce TLS for TCP
cat > pg_hba.conf <<'EOF'
# TYPE  DATABASE  USER  ADDRESS       METHOD
local   all       all                 scram-sha-256
hostssl all       all   0.0.0.0/0     scram-sha-256
hostssl all       all   ::/0          scram-sha-256
EOF

cd ../..
2. Run Postgres with TLS enabled:
docker rm -f local-postgres 2>/dev/null || true

docker run --name local-postgres \
  -e POSTGRES_USER=postgres \
  -e POSTGRES_PASSWORD=postgres \
  -e POSTGRES_DB=app_db \
  -e POSTGRES_INITDB_ARGS="--auth-host=scram-sha-256 --auth-local=scram-sha-256" \
  -p 5432:5432 \
  -v "$PWD/.dev/postgres-tls/server.crt:/var/lib/postgresql/server.crt:ro" \
  -v "$PWD/.dev/postgres-tls/server.key:/var/lib/postgresql/server.key:ro" \
  -v "$PWD/.dev/postgres-tls/pg_hba.conf:/var/lib/postgresql/pg_hba.conf:ro" \
  -d postgres:16 \
  -c ssl=on \
  -c ssl_cert_file=/var/lib/postgresql/server.crt \
  -c ssl_key_file=/var/lib/postgresql/server.key \
  -c hba_file=/var/lib/postgresql/pg_hba.conf
3. Verify TLS works:
docker exec -e PGPASSWORD=postgres local-postgres \
  psql "host=127.0.0.1 port=5432 user=postgres dbname=app_db sslmode=require" \
  -c "select current_setting('ssl') as ssl_on;"
Expected: ssl_on = on. 4. Configure the Wallet Gateway stores to use TLS:
{
    "store": {
        "connection": {
            "type": "postgres",
            "host": "localhost",
            "port": 5432,
            "user": "postgres",
            "password": "postgres",
            "database": "app_db",
            "ssl": { "rejectUnauthorized": false }
        }
    },
    "signingStore": {
        "connection": {
            "type": "postgres",
            "host": "localhost",
            "port": 5432,
            "user": "postgres",
            "password": "postgres",
            "database": "app_signing_db",
            "ssl": { "rejectUnauthorized": false }
        }
    }
}
For production, prefer certificate verification with rejectUnauthorized: true and provide your CA bundle via ssl.ca. See PostgreSQL over TLS/SSL.

Logging

Enable JSON logging with the --log-format flag (pretty is the default):
docker run -p 3030:3030 \
  -v ${PWD}/config.json:/app/config.json:ro \
  ghcr.io/digital-asset/wallet-gateway/docker/wallet-gateway:<VERSION> \
  --log-format=json

After deploying