This guide covers everything needed to run a self-hosted ILN instance — from a local dev environment to a production deployment.
- Prerequisites
- Stellar network requirements
- Contract deployment
- Environment variables
- Local stack with Docker Compose
- Indexer deployment
- Notifications service deployment
- Frontend deployment
- Docs site deployment
- CI/CD setup for a fork
- Minimum viable deployment checklist
| Tool | Version | Install |
|---|---|---|
| Rust | ≥ 1.74 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh |
| wasm32 target | — | rustup target add wasm32-unknown-unknown |
| Stellar CLI | latest | cargo install --locked stellar-cli --features opt |
| Node.js | ≥ 18 | https://nodejs.org |
| pnpm | 9 | npm i -g pnpm@9 |
| Docker + Compose | — | https://docs.docker.com/get-docker |
Run the setup script to install all Rust/Stellar dependencies automatically:
./scripts/dev-setup.shUse the public Soroban testnet RPC — no account setup needed:
RPC_URL=https://soroban-testnet.stellar.org
NETWORK_PASSPHRASE=Test SDF Network ; September 2015
Fund your deployer wallet via Friendbot:
curl "https://friendbot.stellar.org?addr=<YOUR_PUBLIC_KEY>"Use a Soroban-compatible RPC provider. Options:
- Validation Cloud:
https://mainnet.stellar.validationcloud.io/v1/<API_KEY> - Ankr:
https://rpc.ankr.com/stellar_soroban - Self-hosted stellar/quickstart
RPC_URL=https://mainnet.stellar.validationcloud.io/v1/<API_KEY>
NETWORK_PASSPHRASE=Public Global Stellar Network ; September 2015
Ensure the deployer account has ≥ 2 XLM to cover contract deployment fees.
The contract source lives in the ILN-Smart-Contract repo. From the repo root:
stellar contract build
# Output: target/wasm32v1-none/release/invoice_liquidity.wasm# Testnet
npx ts-node scripts/deploy.ts --network=testnet
# Mainnet (requires funded account configured in stellar-cli)
npx ts-node scripts/deploy.ts --network=mainnetThe script:
- Builds the contract WASM
- Deploys via
soroban contract deploy - Writes the resulting contract ID to
.env(CONTRACT_ID=...) andREADME.md
stellar contract invoke \
--id <CONTRACT_ID> \
--network testnet \
-- get_invoice --invoice_id 1
# Expected: InvoiceNotFound error (confirms contract is live)Copy the contract ID into every service's .env:
CONTRACT_ID=<CONTRACT_ID>
NOTIFICATIONS_CONTRACT_ID=<CONTRACT_ID>
| Variable | Description | Example |
|---|---|---|
CONTRACT_ID |
Deployed contract address | CD3TE3... |
NETWORK_PASSPHRASE |
Stellar network passphrase | Test SDF Network ; September 2015 |
RPC_URL |
Soroban RPC endpoint | https://soroban-testnet.stellar.org |
DB_PATH |
SQLite database file path | indexer.db |
POLL_INTERVAL_MS |
Event polling interval (ms) | 5000 |
PORT |
REST API port | 3001 |
START_LEDGER |
Ledger to start from (0 = auto) |
0 |
| Variable | Description | Example |
|---|---|---|
NOTIFICATIONS_DB_PATH |
SQLite database file path | notifications.sqlite |
NOTIFICATIONS_RPC_URL |
Soroban RPC endpoint | https://soroban-testnet.stellar.org |
NOTIFICATIONS_CONTRACT_ID |
Deployed contract address | CD3TE3... |
NOTIFICATIONS_NETWORK_PASSPHRASE |
Stellar network passphrase | Test SDF Network ; September 2015 |
NOTIFICATIONS_POLL_INTERVAL_MS |
Polling interval (ms) | 30000 |
NOTIFICATIONS_START_LEDGER |
Ledger to start from (0 = auto) |
0 |
RESEND_API_KEY |
Resend API key for email delivery | re_... |
RESEND_FROM_EMAIL |
Sender address for notification emails | no-reply@yourdomain.com |
DUE_WARNING_HOURS |
Hours before due date to send warning | 48 |
PORT |
HTTP port | 4001 |
The docker-compose.yml in the repo root starts a complete local stack:
docker-compose up -dServices started:
| Service | Port | Description |
|---|---|---|
stellar |
8000, 11626 | Local Stellar node with Soroban RPC |
indexer |
3001 | Event indexer REST API |
notifications |
4001 | Notification service |
After startup, deploy the contract to the local node:
make deploy-local # builds + deploys contract, writes .local-contract-idThen seed test data:
make seedThe indexer is a Node.js service. It runs anywhere that supports Node ≥ 18.
A railway.toml is already included in indexer/. Connect the repo in the Railway dashboard, set the environment variables from the table above, and deploy.
cd indexer
cp .env.example .env # fill in values
npm install
npm startThe indexer exposes a REST API on PORT (default 3001). Point the frontend's NEXT_PUBLIC_INDEXER_URL at this address.
cd notifications
cp .env.example .env # fill in values, including RESEND_API_KEY
npm install
npm startRuns on PORT (default 4001). No inbound traffic required — it polls the contract directly.
The frontend lives in the ILN-Frontend repo.
| Variable | Description |
|---|---|
NEXT_PUBLIC_CONTRACT_ID |
Deployed contract address |
NEXT_PUBLIC_NETWORK_PASSPHRASE |
Stellar network passphrase |
NEXT_PUBLIC_RPC_URL |
Soroban RPC endpoint |
NEXT_PUBLIC_INDEXER_URL |
Indexer REST API base URL |
vercel --prodOr connect the ILN-Frontend repo directly in the Vercel dashboard. Set the environment variables above under Project → Settings → Environment Variables.
# In ILN-Frontend repo
docker build -t iln-frontend .
docker run -p 3000:3000 \
-e NEXT_PUBLIC_CONTRACT_ID=... \
-e NEXT_PUBLIC_RPC_URL=... \
iln-frontendDocs are Markdown files in docs/. Deploy to GitHub Pages via the existing Actions workflow, or to any static host:
# Example: deploy to Netlify
netlify deploy --dir docs --prodFor custom domains, set the CNAME in your DNS and configure it in the hosting provider.
The repo ships with GitHub Actions workflows in .github/workflows/. After forking:
-
Set repository secrets (Settings → Secrets → Actions):
Secret Used by GITHUB_TOKENAll workflows (auto-provided) PROJECT_PATProject board automation (optional) -
Enable Actions — GitHub disables Actions on forks by default. Go to Actions → enable workflows.
-
Update contract IDs — replace the hardcoded testnet contract IDs in
README.mdandsdk/src/signers.ts(TESTNET_CONTRACT_ID) with your own after deploying. -
Configure branch protection on
main:- Require status checks:
CI / test,CI / build,CI / lint,CI / node-tests - Require pull request before merging
- Require status checks:
Use this checklist to verify a complete deployment before going live.
- Rust ≥ 1.74 and
wasm32-unknown-unknowntarget installed - Stellar CLI installed and
stellar keysconfigured - Deployer wallet funded (≥ 2 XLM on target network)
-
stellar contract buildsucceeds —.wasmfile produced -
npx ts-node scripts/deploy.ts --network=<network>succeeds - Contract ID recorded in
.envandREADME.md -
get_invoiceinvocation returns expectedInvoiceNotFound(contract live)
- All required env vars set (see table)
-
npm startinindexer/runs without errors -
GET http://localhost:3001/invoicesreturns200 []
- All required env vars set (see table)
- Valid
RESEND_API_KEYconfigured - Service starts and polls without errors
-
NEXT_PUBLIC_CONTRACT_ID,NEXT_PUBLIC_RPC_URL,NEXT_PUBLIC_INDEXER_URLset -
npm run buildsucceeds - App loads and connects to Freighter wallet
- Submit invoice flow completes on testnet
- GitHub Actions enabled on the fork
- All CI checks pass on a test PR (
ci.yml) - Branch protection rules configured on
main