Bare Ubuntu box → synced Solana mainnet-beta validator, in one interactive run.
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.
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
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.
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 installThe 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.
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 itselfThat 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 | sha256sumtail -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.
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.
- docs/INSTALL.md — hardware, the disk trade-off, phases 0-8, MEV
- docs/OPERATING.md — commands and
deeploy.conf - docs/UPGRADE.md — upgrades on a staked box
- docs/KEY-MODEL.md — the staked key in full
- docs/OVERLAY.md — private build overlay
- docs/FAILOVER.md — the separate failover tool
Running the tests and the gate: CONTRIBUTING.md.
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.shchanged 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.rs121-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 thesolana-poh-pinunits. - With the data on one RAID volume,
solana.servicedoes not wait for that volume: the unit names its mounts (RequiresMountsFor) only for the two-disk layout, and data mounts arenofail. A boot where the volume does not come up still startssolana.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.
upgradehas run on hardware only on a node that was not voting as the staked identity (above). On one that is, it asks for a typedyesfirst; that path has not run on hardware.dz-connecthands your keypair path todoublezero-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.
MIT — see LICENSE.
Created and maintained by zimone91.