The api subcommand in satellite-modular, it runs the customer and node-facing layer — it’s the front door of the satellite. It handles all real-time requests but does not do any heavy background processing (repair, auditing, accounting rollups, etc.).
See a fun diagram explain how api sign bandwidth on page 53 https://static.storj.io/storjv3.pdf.
For ./satellite-modular api to work, we need at least (it have gazillion flags):
--identity-dir
--database-options.url
--metainfo.database-url
--live-accounting.storage-backend
--orders.encryption-keys
--server.use-peer-ca-whitelist
What are those flags?
--identity-dir ./id/satellite/: if you are using my script on [Tutorial] Run your own satellite (part 3) - Generate certificates.
For --database-options.url and --metainfo.database-url, see [Tutorial] Run your own satellite (part 6) - Database migration, at this time, we don’t do split table yet, if you split it now, you will miss out on tutorial how to hot swap database/table on production.
--live-accounting.storage-backend redis://127.0.0.1:6379, you need to install redis, and for now, we will use redis standalone, I’ve a PR to use redis cluster, but we will see if it merged or if it even work.
--orders.encryption-keys: to generate this key, use this script
--server.use-peer-ca-whitelist: in storj/storj commit 1d63395fd, comment mention to disabling this on satellites entirely, but for reason, no follow up commit ever realize this, you must set this to false.
echo $(openssl rand -hex 8)=$(openssl rand -hex 32) # keep these keys safe
Put it together
./satellite-modular api \
--identity-dir ./id/satellite/ \
--database-options.url "cockroach://my_role:123456@rendezvous.example.com:26257/my_database?sslmode=verify-full&sslrootcert=ca.crt" \
--metainfo.database-url "cockroach://my_role:123456@rendezvous.example.com:26257/my_metainfo_database?sslmode=verify-full&sslrootcert=ca.crt" \
--live-accounting.storage-backend "redis://127.0.0.1:6379" \
--orders.encryption-keys 88a2ab18c619535b=e9bbe3c658d595d5c40dba994152abc07109cdd4425fec627194bdec32c43333 \ # not a real key
--server.use-peer-ca-whitelist=false
For now, this should be put in a systemd file /etc/systemd/system/satellite-api.service:
[Unit]
Description=Satellite API
Requires=network.target
[Service]
Type=simple
WorkingDirectory=/my_satellite/
ExecStart=/my_satellite/satellite-modular api --identity-dir ./id/satellite/ --database-options.url "cockroach://my_role:123456@rendezvous.example.com:26257/my_database?sslmode=verify-full&sslrootcert=ca.crt" --metainfo.database-url "cockroach://my_role:123456@rendezvous.example.com:26257/my_metainfo_database?sslmode=verify-full&sslrootcert=ca.crt" --live-accounting.storage-backend redis://127.0.0.1:6379 --orders.encryption-keys 88a2ab18c619535b=e9bbe3c658d595d5c40dba994152abc07109cdd4425fec627194bdec32c43333 --server.use-peer-ca-whitelist=false
TimeoutStopSec=300
Restart=always
RestartSec=10
User=root
[Install]
WantedBy=default.target
Of course, you have to change to fit your need.
To see your satellite base58-check node ID, use this command:
./identity certificate-authority id --ca.cert-path id/satellite/identity.cert # thanks @alpharabbit
It shall look similar to node ID in https://static.storj.io/dcs-satellites, thing are coming together.
By default, satellite api will listen publicly (0.0.0.0) on port 7777 and privately on port 7778 (127.0.0.1) to communicate with admin service.
If you want to use hashstore (see ./satellite-modular api -h | grep hashstore), set these flags to true:
--contact.hashstore-rollout.current.write-to-new=true \
--contact.hashstore-rollout.next.write-to-new=true \
--contact.hashstore-rollout.next.read-new-first=true \
--contact.hashstore-rollout.next.write-to-new=true \
IMPORTANT UPDATE: if you are using caddy to proxy, it is important to self built it with layer4 support (not the default caddy binary ship with your OS) to proxy tcp on port 7777.
The way I do that on debian/ubuntu - install via apt then use dpkg-divert and mark /usr/bin/caddy to never update let you handle that file, but of course, you can do it however you want.
See you on part 8.
Protip: if you don’t understand what specific flag mean, copy the explaination and paste to github (using “” to search exact), and see the code, if still don’t understand, copy code content and ask AI. It’s 2026!
Update Jul 27:
Q: What to do if your new satellite have enough nodes but not enough unique /24 subnet?
A: There are many knots to try, but personally I use this:
overlay.node.distinct-ip # for satellite-modular api
overlay.node.new-node-fraction # for satellite-modular api and repair
After a while, use this sql to confirm if you have enough unique subnets then remove config above:
SELECT count(DISTINCT regexp_replace(last_ip_port, '\.\d+:\d+$', ''))
AS distinct_24s, count(*) AS nodes FROM nodes
WHERE last_contact_success > now() - interval '4 hours'
AND disqualified IS NULL
AND last_ip_port NOT LIKE '[%';
Update Aug 3:
If you are running a test satellite, you should set price config so storagenode operator don’t get supprised about it:
# config.yaml
compensation:
rates:
at-rest-gb-hours: "0"
get-audit-tb: "0"
get-repair-tb: "0"
get-tb: "0"
put-repair-tb: "0"
put-tb: "0"