A desktop app that tunes Bitaxe miners running official AxeOS. It watches each miner's AxeOS API and moves frequency and voltage toward the goal you pick (the most hashrate, a balance, or the lowest J/TH) without crossing that board's limits. It runs on Windows, macOS, and Linux as a local window. The page is a file inside that window; nothing listens on the network.
Everything stays on your computer. There is no account, no analytics, and no telemetry, and the author collects no data of any kind. A new install talks only to your miners until you switch on an internet feature yourself. See Privacy.
It started as one owner's tuner for six repasted, custom-cooled Gamma 601s on an oversized 5 V supply. That upgraded setup is the only hardware it has run on so far. A stock Gamma 601, and every other board, has not been tested yet; Support the project says how to help with that. docs/decisions.md lists every choice made for the author's fleet and what other boards get instead, and docs/roadmap.md lists what comes next.
Until October 2026 it was called Groundhog Gamma Tuner. It was renamed when it grew past the Gamma.
Your data stays on your computer. The app has no account, no sign-up, no analytics, no telemetry, and no crash reports. The author never receives anything from it, not even a count of who runs it.
- What it keeps: two files on your computer.
config.jsonholds the settings and the miner list, andhistory.dbholds the history. Delete them and nothing is left. - Your miners: the app reads and writes each miner's AxeOS API on your local network. That traffic stays on your network.
- The window: the page is a file inside the app. It loads nothing from the internet, and the app opens no port.
A new install talks only to your miners. Each feature below reaches one outside service, and only after you switch it on, at first start or in Global Settings > Internet:
| Feature | Service | What it sends | How often |
|---|---|---|---|
| Outdoor weather | Open-Meteo | The place you picked | Every 15 minutes |
| Network difficulty | mempool.space | Only the request | Every minute |
| Pool status | The pools your miners use | A connection that closes at once, like a miner opening one | Every minute |
| AxeOS update notice | GitHub | Only the request | Once a day |
Like any internet request, each one shows that service your public IP address.
The weather location is only ever one you set yourself:
- Search: what you type goes to Open-Meteo's place search.
- Use This Device's Location (Windows): Windows location services find the computer. The app rounds the position to about 1 km before it saves or sends it, then asks Nominatim (OpenStreetMap) once for the place name.
- Forget Location in the same dialog removes it.
Updating from an older version keeps these features on, as they were. Turn any of them off in Global Settings > Internet.
Every board in the official AxeOS v2.15.3 board list. The app reads the board version and ASIC from the miner and refuses anything else, including firmware forks.
| Family | Board versions | ASIC | Input | Status |
|---|---|---|---|---|
| Gamma | 601 | BM1370 | 5 V | Verified |
| Gamma | 600, 602, 603 | BM1370 | 5 V | Experimental |
| Gamma Duo | 650 | 2 × BM1370 | 5 V | Experimental |
| Gamma Turbo | 801 | 2 × BM1370 | 12 V | Experimental |
| Gamma Hex | 1300 | 6 × BM1370 | 12 V | Experimental |
| Supra | 400, 401, 402, 403 | BM1368 | 5 V | Experimental |
| Supra Hex | 701, 702 | 6 × BM1368 | 12 V | Experimental |
| Ultra | 0.11, 201–205, 207 | BM1366 | 5 V | Experimental |
| Hex | 302, 303 | 6 × BM1366 | 12 V | Experimental |
| Naja Duo | 1201 | 2 × BM1373 | 12 V | Experimental |
| Max | 2.2, 102 | BM1397 | 5 V | Experimental |
Verified means it has run on the author's upgraded Gamma 601s: repasted, custom cooling, and a supply with room to spare. A 601 with its stock cooler has not run this tuner yet.
Experimental means nobody has run that board with this tuner yet:
- Its voltage stops at the ASIC's top AxeOS preset (1250 mV on a BM1370), and its frequency at 1.5 × the top frequency preset.
- The first Start asks you to confirm, once per miner.
- Boards without a regulator sensor (Max, Ultra 0.11–205, Supra 400/401) are judged on the ASIC alone, and their core-current limit is off. Their
currentreading is input current. - If you run one, please report how it went so it can be marked verified. You can also send one to be tested.
Firmware: official AxeOS v2.11.0 or newer. Older firmware uses a different fan setting and is refused. Versions newer than v2.15.x tune with a warning in the log.
Each miner has a mode. You pick one for new miners on first start (or in Global Settings), and per miner in AutoTuner Settings. Picking a mode fills in its limits; you can still change any of them, and the form then says "Custom limits".
| Mode | Goal | Fan | Voltage ceiling | Default caps |
|---|---|---|---|---|
| Max hashrate | Most hashrate | Manual 100% | The board's hard cap (1500 mV on a Gamma 601) | 70 °C ASIC, 95 °C regulator |
| Balanced | More hashrate inside stock-cooler temperatures | AxeOS auto | The top AxeOS preset | 65 °C ASIC, 85 °C regulator, regulator warning current |
| Efficiency | Lowest J/TH | AxeOS auto | Stock voltage | 65 °C ASIC, 85 °C regulator |
- Max hashrate is for custom cooling and a supply that can take it. It climbs until heat, chip errors, power, or core current stops it. It is not for a stock cooler.
- Balanced uses the same climbing logic with stock-cooler limits. AxeOS's own fan control aims 5 °C under the ASIC cap.
- Efficiency never raises voltage above stock. Chip errors cost a clock, not more voltage, and voltage is trimmed while energy per good hash goes down. At a heat wall it trims voltage instead of only waiting. When a trim alone adds errors, it tries the trim again two clock steps lower. It usually settles near the lowest voltage the chip runs well. On a stock Gamma that is fewer TH/s for noticeably less power.
tools/simulate.py runs the real tuner against a simulated Gamma 601 on a fast clock, so you can compare the modes in seconds:
python tools/simulate.py --cooling stock --ambient 24 --hours 8Prebuilt apps are attached to each release: Windows (x64; also runs on Windows on ARM), macOS (Apple silicon), and Linux (x64). They are not code-signed. No release has been published yet; until the first one, run it from source.
- Windows: if SmartScreen says "Windows protected your PC", choose More info, then Run anyway.
- macOS: if it says the app cannot be opened, right-click it, choose Open, then Open again.
- Linux: extract the archive and run
Groundhog Tunerinside the folder.
A downloaded app keeps config.json and history.db in your user data folder:
- Windows:
%APPDATA%\Groundhog Tuner - macOS:
~/Library/Application Support/Groundhog Tuner - Linux:
~/.config/groundhog-tuner
Run from a source checkout, the app keeps its settings next to the scripts. It needs Python 3.13 or newer and the two packages in requirements.txt (requests and pywebview).
Windows 10 or 11, on a regular (x64) PC or a Windows on ARM machine.
- Python: the Windows installer (64-bit) from python.org. On a regular PC that is the usual download. On a Windows on ARM machine, use it rather than the ARM64 installer: the x64 build runs under emulation, but the ARM64 build cannot open the window because pywebview's .NET helper does not load in it. If the ARM64 build is the
pythonon PATH,main.pyrestarts itself in the x64 one. - WebView2: Windows 11 includes the Edge WebView2 runtime, and most Windows 10 PCs have it through Edge. If the window does not open on Windows 10, install the Evergreen WebView2 Runtime from Microsoft.
Open Command Prompt in this folder:
python -m pip install -U -r requirements.txt
python main.pyOn a Windows on ARM machine, pip on PATH may be the ARM64 one. py -0p lists the interpreters; install with the one whose folder does not end in -arm64, for example "%LocalAppData%\Programs\Python\Python313\python.exe" -m pip install -U -r requirements.txt.
- Desktop shortcut: double-click
install-shortcut.bat. The shortcut runsrun.bat, which starts the window with no console. - Start at logon: in Task Scheduler (
taskschd.msc), create a basic task that runsrun.batwhen you log on. On the Conditions tab, clear Start the task only if the computer is on AC power.
Install Python 3.13 from python.org or Homebrew, then:
python3 -m venv .venv
.venv/bin/python -m pip install -U -r requirements.txt
./run.sh- Open from Finder: double-click
Groundhog Tuner.command. - Start at login: add that file under System Settings > General > Login Items.
The window uses GTK and WebKit from your distribution:
| Distribution | Packages |
|---|---|
| Debian, Ubuntu | sudo apt install python3-gi python3-gi-cairo gir1.2-gtk-3.0 gir1.2-webkit2-4.1 |
| Fedora | sudo dnf install python3-gobject webkit2gtk4.1 |
| Arch | sudo pacman -S python-gobject webkit2gtk-4.1 |
Then make a virtual environment that can see those packages, and run:
python3 -m venv --system-site-packages .venv
.venv/bin/python -m pip install -U -r requirements.txt
./run.sh- No GTK:
.venv/bin/python -m pip install "pywebview[qt]"uses Qt instead. - Application menu:
./install-shortcut.shadds the app../install-shortcut.sh --autostartalso opens it when you log in.
An update never changes a value you saved: limits, modes, start clocks, nicknames, and location stay as they are.
- Downloaded app: replace it with the new version.
config.jsonandhistory.dbstay in your user data folder. - From source: run
git pull, thenpython -m pip install -U -r requirements.txt.config.jsonandhistory.dbare not in git, so a pull never touches them. Do not rungit clean -x, which deletes them. - A copy cloned before 2026-10-08: the published history was cleaned that day, so
git pullreports that the branches have diverged. Rungit fetch originand thengit reset --hard origin/main. Neither touchesconfig.jsonorhistory.db. Copy both somewhere safe first anyway: on a copy cloned before 2026-09-22, git itself still tracksconfig.json. - New settings: a newer version can add settings, each set to match how your older version behaved, and drop settings nothing uses any more. Before it rewrites
config.jsonfor that, it keeps the old file next to it asconfig.backup-v<N>.json. - Your miners: the app writes nothing to a miner until you press Start Autotuner. The one exception is a cooled miner that AxeOS left in overheat mode: the app clears that flag. With Continue from current clocks on (the default), each miner picks up from the clocks it is running.
On first start the app asks four questions:
- Cooling: stock, upgraded, or custom.
- What you want most: the most hashrate, a balance, or the lowest J/TH. Together with cooling this picks the mode for new miners. Max hashrate needs custom cooling and an explicit OK; otherwise "most hashrate" gives Balanced.
- Watts per miner: what your supply and wiring can give each miner. The tuner steps down above it. Blank uses each board's AxeOS max power (40 W on a Gamma).
- Privacy: which internet features to switch on, each with what it is for. All of them start off (see Privacy).
If you switch on outdoor weather, the app explains why it needs your location and asks for it: type a city, or on Windows let the computer find it. Then Scan Network finds the miners on your subnet, or you add one by IP. AutoTuner Settings shows each miner's mode and limits. Start Autotuner starts tuning.
- Opening. The app checks the live board and firmware, enables overclocking, and sets the fan for the mode. Nothing learned in an earlier session is saved; weather, paste, and voltage change too much between runs. A miner already hashing inside its limits continues from the clocks it runs now. A miner that is off, in overheat mode, faulted, or outside its limits starts from its start clocks (its board's stock clocks unless you change them). Continue from current clocks in Global Settings turns this off.
- Fast start (Global Settings, on by default). The app jumps frequency and voltage toward a point under the ASIC and regulator caps: 6 °C under in Max mode, 8 °C with auto fan. It holds each jump about a minute, and fits a line through the watts and temperatures it saw to aim the next one. Any limit crossed on the way sends it back to the last clocks that held. A cool chip reaches its sweet spot in minutes instead of hours.
- Steps down. Frequency comes down when ASIC temperature, regulator temperature, power, core current, input voltage, or core-voltage droop crosses its limit.
- When the input voltage stops it, the Phase column says "held back by the supply" with the reading that stopped it, for example 4.89 V at 18 W. Then the supply or its wiring is the limit, not the chip. Hover over it to see what to check. Core-voltage droop gets its own hover too.
- The tuner never climbs or raises voltage into the core-current cap. The cap stays under the regulator's no-retry shutdown: 29 A of 30 A on a Gamma.
- Temperature caps stay at least 4 °C under the AxeOS overheat trip (75 °C ASIC, 105 °C regulator). Inside that margin the tuner sheds 20 MHz and 10 mV every 30 seconds, so AxeOS does not cut power and drop the clocks 100 MHz and 100 mV on its own.
- If AxeOS does trip, the tuner puts the clocks back on the saved minimum once the chip is cool. If AxeOS leaves the chip off in overheat mode, the tuner writes the minimum clocks, clears the flag, and restarts the miner once. In an auto-fan mode it also turns auto fan back on, which AxeOS switches off on a trip.
- Steps up.
- In Max hashrate and Balanced, voltage rises only when the ASIC error percentage is over the budget (2%). Frequency rises while errors stay inside it.
- A cool chip climbs up to 4 steps (20 MHz) per settle. Near a cap, or with errors over half the budget, it climbs one step.
- Each step is kept only if good hashrate held, or in Efficiency mode, only if energy per good hash held.
- Holds. At the frequency ceiling it trims voltage, then holds. The Phase column shows what is holding it. A hold under a hashrate or silicon wall climbs again once the chip is 3 °C cooler, or after 6 hours, while errors are at most half the budget.
- Recovers. It restarts a miner once when its settled errors stay far over the budget (10%), which AxeOS can leave after an overheat recovery. If a soft restart does not clear it, unplug the miner for 30 seconds.
The Limits screen shows each board's firmware, regulator, and chip limits, the tuner's hard range, and every mode's preset.
The History tab shows how each miner ran.
- While the window is open, every miner is saved every 10 minutes in
history.dbnext toconfig.json. Each sample has the clock, the 10-minute hashrate less the error share, the temperatures, and the power. A sample counts as settled once its setpoint and the miner's uptime are at least 10 minutes old, and the results use only settled samples. - The filters pick one miner or the whole fleet, a period (24 hours to everything, Since reset, or Since change), and a measure: good hashrate, efficiency (J/TH, lower is better), or clock.
- Outdoor weather is optional and off until you turn it on, in the first-start questions or in Global Settings > Internet. It then comes from Open-Meteo every 15 minutes, free for personal use and under CC BY 4.0, and gaps are filled from Open-Meteo's hourly history up to 92 days back. When it runs best and Best combinations compare results against outdoor temperature, time of day, and sky. That matters most when the miners breathe outdoor air.
- Settings > Weather Location sets the place. Search for your city, or on Windows use Use This Device's Location. That needs Location on, with Let desktop apps access your location on. The app never looks up a location on its own. Forget Location removes the saved place.
Double-click a miner (or right-click and choose Reset to Baseline) to write its board's stock clocks and make them its start clocks. Settings > Reset All to Baseline does the same for every miner. Both ask first, and the reset is marked in the history. For new paste, a new cooler, or a new supply, set the date under Edit Miner ("Hardware changed on"). Since change then starts there.
A miner shows as offline after three missed reads in a row (about 15 seconds). One missed read shows "no reply" under its phase. A reboot or a Wi-Fi blip looks like that.
The Gamma 601 limits come from six repasted, custom-cooled boards on one Mean Well RSP-320-5 (5 V, 60 A):
- Max hashrate, verified on the 601: 70 °C ASIC, 95 °C regulator, up to 1500 mV and 1100 MHz, and 29 A core current.
- Watt cap: 50 W per miner, as a runaway guard; the supply is not the limit.
- Input floor: 4.9 V, because the 5 V input sags in the wiring.
These numbers are for that cooling and that supply. A stock Gamma, a weaker supply, or a warm room needs other limits, which is what Balanced and Efficiency are for. docs/decisions.md has every choice with its reason.
Install the checkers with the same interpreter, then add the push hook:
python -m pip install -r requirements-dev.txt
pre-commit installgit push then runs ruff check, ruff format --check, and python -m unittest, and stops if any fail. Run the same checks by hand with pre-commit run --all-files --hook-stage pre-push. GitHub Actions runs them on Windows, macOS, and Linux for every push and pull request.
Every line and branch of the app is covered by a test. GitHub Actions measures it, and the Linux job fails under 100%. Check it before you push:
python -m coverage run -m unittest
python -m coverage report-
Updates keep saved values:
test_upgrade.pyloadsconfig.jsonas each earlier version saved it, from the original upstream app on, and checks that every value stays, that the old file is kept as a backup, and that the next start changes nothing. -
Rare tuner paths:
test_autotune_session_paths.pydrives real tuning sessions against a scripted miner: silent replies, refused writes, trips, and a stop at each wait. -
Releases: until the first release,
config.jsonandhistory.dbmay change shape without a migration. Before tagging a release, runpython tools/snapshot_release.py vX.Y.Zon the code being released and committest_fixtures/released/vX.Y.Z. The release build refuses a tag without a matching snapshot. From then on,test_upgrade.pyloads every released snapshot with the current code. A change that breaks one needs a migration (aCONFIG_VERSIONstep inconfig.py, or a schema step inhistory.py). Never edit or regenerate a released snapshot. -
The author's fleet:
test_fleet.pypins how a Gamma 601 in Max hashrate mode is tuned to a fingerprint of the tuner's answers taken before other boards and modes existed. A change that moves it changes that fleet's tuning. -
Every board and mode:
test_modes.pychecks that each preset stays inside its board's limits, and that randomized readings never push a decision past them. -
A board AxeOS adds later: copy its row from that release's
main/device_config.hintoboards.py. A board someone has verified gets its own entry inVERIFIED_LIMITS. -
Modes and their presets live in
modes.py. -
OS-specific code (notifications, the clock format, window placement) lives in
desktop.py. -
Prebuilt apps:
pyinstaller --noconfirm packaging/groundhog-tuner.specbuilds the app for the OS you run it on. Pushing av*tag builds all three on GitHub and opens a draft release.
The tuner has only run on the author's own upgraded Gamma 601s. Every other board, and every stock setup, needs a real unit to test on.
Sponsorships and bitcoin pay for this project only: test boards and other hardware, development costs, and subscriptions.
GitHub Sponsors takes one-time or monthly support. GitHub keeps no fee on sponsorships from personal accounts.
bc1qukvdcnwacf7v2w3uwedq4hugs4p38f5hwncjmh
- On-chain bitcoin only. Do not send Lightning payments or tokens on other chains to it.
- Payments are public on the blockchain but carry no name. To be listed as a supporter, email the transaction ID and the name to use to huldoser@gmail.com.
- Wanted: any experimental board from the table above, or a stock Gamma 601 with its stock cooler, so Balanced and Efficiency get tested on the setup they are made for.
- How: open a hardware offer with the board and its setup, or email huldoser@gmail.com. Shipping details go by email. Do not post an address in the issue.
- What happens to it: the board stays with the author as a test board and is not sent back. It runs with the tuner, the result is posted in its issue, and after a clean run its row in the table says Verified. There is no set date. A board that does not run cleanly stays experimental until the tuner handles it.
Public sponsors and everyone who sent a board are listed here, under the name they choose. Nobody is listed without asking.
None yet.
This project began as a fork of bitaxe-temp-monitor by Hurllz and is now developed separately at Huldoser/groundhog-tuner. Copyright in the original work remains with Hurllz. Copyright in these modifications is held by Andrey Rychkov.
The upstream project also includes work by DeanCollier, Andrew Kuehne (andewkuehne), mrv777, and GUI work credited to Birdman332. The headless web server and Docker setup from that project are not in this project.
This is an unofficial tool. It is not affiliated with Hurllz, the Bitaxe project, or ESP-Miner.
This program writes frequency, voltage, fan, and overclock settings to your miners.
- Max hashrate runs above stock clocks and voltage. It is meant for custom cooling and a supply and wiring rated for it.
- Balanced and Efficiency stay inside stock-cooler temperatures, but still change clocks AxeOS would not change on its own.
- Experimental boards have not been run with this tuner by anyone yet.
Overclocking can overheat the ASIC, the regulator, the board, the plug, or the wiring. That can damage hardware or start a fire. A stock cooler, a weak supply, a loose plug, or a warm room makes that more likely. You are responsible for the cooling, power, and limits on your own hardware. Read the settings before you start, and lower them if you are not sure.
The software is provided as is, without warranty. Andrey Rychkov and the other authors are not responsible for damaged hardware, injury, fire, lost mining, or any other loss from using it. The legal warranty disclaimer is in the MIT License.
MIT. Copyright (c) 2025 Hurllz and Copyright (c) 2026 Andrey Rychkov. See LICENSE.


