Skip to content
zimone91Public

About

Interactive, idempotent bash CLI that takes a bare Ubuntu box to a synced Solana mainnet-beta validator (Agave + Jito-BAM, built from source) — tuned, verified, and ready for a manual staked-key swap. Your staked key is never generated, copied, or transmitted.

Topics

Resources

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

DeePloy

Bare Ubuntu box → synced Solana mainnet-beta validator, in one interactive run.

CI tests shellcheck status release license

DeePloy takes a fresh Ubuntu 24.04 server to a synced Agave + Jito-BAM mainnet-beta validator at catchup 0. It builds the client from source, tunes the box for it, and leaves the staked-key swap to you.

What you need

Ubuntu 24.04 x86-64 · root · two data NVMe + a system disk
one reboot · a disk wipe you confirm by typing yes · ~11-12 min build
every run so far: 48 threads, 377 GiB RAM, 2× 1.92 TB data NVMe — the build time too

⚠️ Your staked key

DeePloy never generates, copies, moves, or transmits your staked key, and it won't put the key on the box for you. The node syncs on a throwaway identity; you swap the real key in yourself at the end.

In file mode, the default, DeePloy reads the keypair file you place on the box, from two commands. In phase 5, install checks whether the file is there; if it is, it derives the public key once, to check it is not the sync identity, and once more if you hand it a DoubleZero ID to copy. dz-connect derives it before each attempt to install the DoubleZero ID and once more for the passport, and signs one passport message with it. Those reads are local and read-only. In offbox mode there is no keypair file on the box for DeePloy to read: it keeps only the public key, and you pipe the key to set-identity over ssh from the machine that holds it.

In file mode, dz-connect then passes the keypair path to doublezero-solana, which DeePloy does not control. That handoff, and the reasoning behind each read, are in docs/KEY-MODEL.md. Skip DoubleZero and it never happens.

Install (one command)

sh -c "$(curl -sSfL https://zim.one/deeploy/v0.1.1)"

That downloads the pinned release and its SHA256SUMS, checks the tarball against the manifest, and unpacks it into ./deeploy-v0.1.1. It refuses to start if that directory already exists. With sudo available it hands the checkout to root; without it, it prints the two commands for you to run.

Then it stops. DeePloy wipes disks, rewrites GRUB and reboots the box, so you read the checkout and start the install yourself:

cd deeploy-v0.1.1
sudo ./deeploy.sh install --dry-run   # the plan, up to the reboot
sudo ./deeploy.sh install

The dry-run asks the questions the install asks, the typed yes before a disk is erased among them, so it needs a terminal, as the install does. It ends where the install reboots to apply the CPU isolation, and it changes nothing outside a temp dir that it removes on exit.

The paranoid path, which is the one to take for a tool that runs as root:

curl -fsSLO https://raw.githubusercontent.com/zimone91/deeploy/v0.1.1/get-deeploy.sh

Read it, then run sh get-deeploy.sh. Or from source:

git clone --branch v0.1.1 https://github.com/zimone91/deeploy
cd deeploy
sudo chown -R root:root . && sudo chmod -R go-w .
sudo ./deeploy.sh install --dry-run

The chown there is not tidiness. The post-reboot resume service runs this checkout as root at boot, so phase 0 refuses to install it out of a directory another local user could write to.

The chmod is not tidiness either. Under some umasks — 002 is the common one — a fresh clone or an unpacked tarball comes out group-writable, and the same two commands above remove it.

Verify what you downloaded

Release tarballs ship a SHA256SUMS next to them, produced by the tagged release workflow from git archive of that exact tag:

sha256sum -c SHA256SUMS        # must print: OK
tar tzf deeploy-vX.Y.Z.tar.gz | grep -vc '/$'   # how many files, directories aside
tar tzf deeploy-vX.Y.Z.tar.gz                   # then read the list itself

That list should hold no submodules, no binaries, no build output, and nothing whose name you cannot place.

Tarball and manifest travel the same channel, so that pair catches a damaged or altered download rather than a compromised repository. get-deeploy.sh travels a different one, served from zim.one, and its digest is published in the release notes. Comparing those two is the check that says something about origin:

curl -sSfL https://zim.one/deeploy/v0.1.1 | tail -n +2 | sha256sum

tail -n +2 drops the one line the endpoint adds to pin the version. Tags are annotated but not signed, so a checksum proves integrity, not authorship.

What it leaves on the box

DeePloy is not a daemon and does not supervise the validator. solana.service runs the node. The rest of what it installs is DeePloy's own and keeps running without it: performance-tweaks.service and solana-sysctl.service reapply host tuning at every boot, and an mlx5 or bnxt_en NIC adds a tuning unit. Phase 2 also leaves exactly one GOVERNOR="performance" line in /etc/default/cpufrequtils and keeps the file's other lines.

Where needrestart is installed, phase 1 writes /etc/needrestart/conf.d/90-deeploy-solana.conf, so that needrestart never restarts solana.service after a library update: on Ubuntu 24.04 the APT hook runs needrestart in automatic mode, and the validator maps libc. Restart the validator at a planned time; a staked node goes through its standby first.

One unit runs DeePloy itself. deeploy-resume.service carries the install across its single reboot. The automatic resume disables it; a manual install --resume does not, so after one of those run systemctl disable deeploy-resume.service. Neither path deletes the unit file.

Documentation

Running the tests and the gate: CONTRIBUTING.md.

Status & known limitations

DeePloy 0.1.1 is the first release after the release-candidate series: there is no 0.1.0, the series ended at 0.1.0-rc10. The full cycle, from install through catchup 0 to the key swap, was proven on two production boxes in June 2026. 0.1.0-rc9 ran on one mainnet box on 2026-10-05, from install to catchup 0 on the throwaway identity, with verify and an upgrade that applied a private overlay; on 2026-10-06 its staked key was swapped in by hand. On that box the upgrade of 0.1.0-rc10 then ran twice, each time while the box was on its sync identity and the staked key voted on another node: on 2026-10-13 to 4.3.0-jito.1 with a private overlay, and on 2026-10-19 to 4.3.2-jito with none.

Mainnet-beta has run Alpenglow since epoch 1064 (2026-10-21). On 2026-10-23 revision 7b5d812 of this code installed a second mainnet box from scratch: phases 0 to 8 (0 to 6 one at a time, 7 skipped: DoubleZero was not enabled) with the reboot between 7 and 8, catchup 0 on the throwaway identity, and verify with the vote account's BLS key and VAT balance read from the chain. That box did not vote then. On 2026-10-24 and 2026-10-25 phases 1, 2 and 6 were run on it again at later revisions, then a reboot and phase 8. Nothing after those revisions has been executed on hardware, the revision you are reading included: the stored commission as the default of a phase 6 re-run, the DoubleZero multicast flag as DeePloy renders it (the same form, written by hand, has run on a mainnet node), jq in phase 4 and the pin. Of the changes before them, the needrestart deferral itself (the file was written on the box, no package was upgraded there), the preflight's online-CPU count and the warning for a leftover POH_CORE did not run there either. The pinned jito-solana 4.4.0-jito has run on two mainnet nodes since 2026-10-30, built there by the operator from the tag; nobody has built it with DeePloy.

  • Agave 4.2 inverted the AF_XDP default, so the generated validator.sh changed on a hardware-proven path. The new form ran on an mlx5 box with zero-copy on 2026-10-05. On bnxt_en, which has no zero-copy, it ran from 2026-10-23 on a box that did not vote then; read on 2026-10-24, the validator held two AF_XDP sockets.
  • On bnxt_en, a failed AF_XDP check prints "Do not start the validator." and the install goes on: phase 8 starts the validator anyway.
  • DeePloy sets no PoH core and pins no PoH thread: after the Alpenglow migration, jito-solana 4.4.0-rc.1-jito does not start PohService (poh/src/poh_service.rs 121-129), so there is nothing to pin. Phase 2 isolates the XDP cores and their SMT siblings only. Over an older install, re-running phase 2 strips the old PoH core from GRUB (it takes effect at the next reboot) and re-running phase 6 removes the solana-poh-pin units.
  • With the data on one RAID volume, solana.service does not wait for that volume: the unit names its mounts (RequiresMountsFor) only for the two-disk layout, and data mounts are nofail. A boot where the volume does not come up still starts solana.service.
  • The hot swap and the cold bring-up that DeePloy prints follow the jito-solana code under Alpenglow and are tested, not executed: no box has run them as this version prints them.
  • upgrade has run on hardware only on a node that was not voting as the staked identity (above). On one that is, it asks for a typed yes first; that path has not run on hardware.
  • dz-connect hands your keypair path to doublezero-solana, which comes from an apt repository and is not pinned here.
  • The build downloads rustup-init, the anza installer, jito-solana and a clone of the XDP helper, and runs all of it as root without checking a checksum or a signature. The one exception is the pinned jito-solana tag, which must check out the commit DeePloy pinned it to; any other tag is built as it is.
  • Disk eligibility is checked against the layouts this has run on. Read the table it prints before you type yes.
  • You are responsible for your keys, your stake, and your slots. No warranty.

Vulnerabilities go through the private advisory link in SECURITY.md, not a public issue.

License

MIT — see LICENSE.


Created and maintained by zimone91.

About

Interactive, idempotent bash CLI that takes a bare Ubuntu box to a synced Solana mainnet-beta validator (Agave + Jito-BAM, built from source) — tuned, verified, and ready for a manual staked-key swap. Your staked key is never generated, copied, or transmitted.

Topics

Resources

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages