#!/bin/bash # # Bitcoin Solo Miner Appliance Installer # An Open Source Appliance from Robbie Ferguson # (c) 2025 Robbie Ferguson set -e APP_ROOT="/opt/btc-solo" WWW_ROOT="/var/www/btc-solo" BITCOIN_CONF="/etc/bitcoin/bitcoin.conf" CERT_DIR="/etc/ssl/localcerts" CKPOOL_USER="ckpool" CKPOOL_PORT="3333" BITCOIN_DATA="/var/lib/bitcoind" # --------------------------------------------------------------------------- # PURGE # --------------------------------------------------------------------------- if [[ "$1" == "--purge" ]]; then systemctl stop ckpool 2>/dev/null || true systemctl stop bitcoind 2>/dev/null || true rm -f /var/lib/btc-solo/.setup-complete rm -f /etc/systemd/system/ckpool.service rm -f /etc/systemd/system/bitcoind.service rm -f /etc/nginx/sites-enabled/btc-solo rm -f /etc/nginx/sites-available/btc-solo rm -f /etc/sudoers.d/btc-solo rm -rf "$APP_ROOT" "$WWW_ROOT" "$CERT_DIR" "$BITCOIN_DATA" rm -f /usr/local/sbin/btc-apply-admin.sh /usr/local/sbin/btc-set-prune.sh /usr/local/sbin/btc-make-cert.sh # Remove bitcoin binaries we installed directly rm -f /usr/local/bin/bitcoind /usr/local/bin/bitcoin-cli /usr/local/bin/bitcoin-tx /usr/local/bin/bitcoin-util 2>/dev/null || true systemctl daemon-reload systemctl restart nginx 2>/dev/null || true echo "Bitcoin Solo Miner Appliance purged." exit 0 fi if [[ $EUID -ne 0 ]]; then echo "Run as root." exit 1 fi echo "[1/8] Updating apt…" apt-get update echo "[2/8] Installing base packages…" apt-get install -y \ curl ca-certificates gnupg \ nginx php-fpm php-cli \ git build-essential autoconf automake libtool pkg-config \ libjansson-dev libevent-dev libcurl4-openssl-dev libssl-dev zlib1g-dev \ openssl # --------------------------------------------------------------------------- # [3/8] Install Bitcoin Core from official binaries # --------------------------------------------------------------------------- echo "[3/8] Installing official Bitcoin Core binaries…" BTC_VER="27.0" BTC_BASE="bitcoin-${BTC_VER}" BTC_TAR="${BTC_BASE}-x86_64-linux-gnu.tar.gz" BTC_URL="https://bitcoincore.org/bin/bitcoin-core-${BTC_VER}/${BTC_TAR}" BTC_SUMS_URL="https://bitcoincore.org/bin/bitcoin-core-${BTC_VER}/SHA256SUMS" mkdir -p /tmp/bitcoin cd /tmp/bitcoin echo "Downloading Bitcoin Core ${BTC_VER}…" curl -LO "${BTC_URL}" curl -LO "${BTC_SUMS_URL}" echo "Verifying checksum…" grep "${BTC_TAR}" SHA256SUMS | sha256sum -c - echo "Extracting…" tar -xzf "${BTC_TAR}" install -m 0755 -o root -g root ${BTC_BASE}/bin/* /usr/local/bin/ # systemd service for bitcoind cat > /etc/systemd/system/bitcoind.service < "$BITCOIN_CONF" </dev/null 2>&1 || useradd -r -s /bin/false "$CKPOOL_USER" mkdir -p "$APP_ROOT/conf" "$APP_ROOT/logs" cat > "$APP_ROOT/conf/ckpool.conf" < /etc/systemd/system/ckpool.service < /usr/local/sbin/btc-apply-admin.sh <<'EOF' #!/bin/bash CONF="/etc/bitcoin/bitcoin.conf" CKPOOL_CONF="/opt/btc-solo/conf/ckpool.conf" ADMINUSER="$1" ADMINPASS="$2" if [[ -z "$ADMINUSER" || -z "$ADMINPASS" ]]; then echo "Usage: $0 " exit 1 fi # Update bitcoin.conf sed -i '/^rpcuser=/d' "$CONF" sed -i '/^rpcpassword=/d' "$CONF" echo "rpcuser=${ADMINUSER}" >> "$CONF" echo "rpcpassword=${ADMINPASS}" >> "$CONF" # Update ckpool config so it can still talk to bitcoind if [[ -f "$CKPOOL_CONF" ]]; then sed -i "s/\"user\"[[:space:]]*:[[:space:]]*\"[^\"]*\"/\"user\" : \"${ADMINUSER}\"/" "$CKPOOL_CONF" sed -i "s/\"pass\"[[:space:]]*:[[:space:]]*\"[^\"]*\"/\"pass\" : \"${ADMINPASS}\"/" "$CKPOOL_CONF" fi systemctl restart bitcoind systemctl restart ckpool EOF chmod 700 /usr/local/sbin/btc-apply-admin.sh # set prune size cat > /usr/local/sbin/btc-set-prune.sh <<'EOF' #!/bin/bash CONF="/etc/bitcoin/bitcoin.conf" SIZE="$1" if [[ -z "$SIZE" ]]; then echo "Usage: $0 " exit 1 fi sed -i '/^prune=/d' "$CONF" if [[ "$SIZE" != "full" ]]; then echo "prune=${SIZE}" >> "$CONF" fi systemctl restart bitcoind EOF chmod 700 /usr/local/sbin/btc-set-prune.sh # setup donation system cat > /usr/local/sbin/btc-set-donation.sh <<'EOF' #!/bin/bash CONF="/opt/btc-solo/conf/ckpool.conf" ADDR="$1" RATE="$2" if [[ -z "$ADDR" || -z "$RATE" ]]; then echo "Usage: $0 " exit 1 fi # ensure conf exists if [[ ! -f "$CONF" ]]; then echo "ckpool.conf not found at $CONF" exit 1 fi # remove old lines sed -i '/"donaddress"/d' "$CONF" sed -i '/"donrate"/d' "$CONF" # add new ones just before the final } # (very simple appender; good enough for our generated file) sed -i '$i \ ,"donaddress" : "'"$ADDR"'",\n "donrate" : '"$RATE"'' "$CONF" systemctl restart ckpool EOF chmod 700 /usr/local/sbin/btc-set-donation.sh # (re)create self-signed cert cat > /usr/local/sbin/btc-make-cert.sh <<'EOF' #!/bin/bash CERTDIR="/etc/ssl/localcerts" mkdir -p "$CERTDIR" openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \ -keyout ${CERTDIR}/btc-solo.key \ -out ${CERTDIR}/btc-solo.crt \ -subj "/C=CA/ST=Ontario/L=Barrie/O=BTC-Solo/OU=IT/CN=$(hostname -f)" chmod 600 ${CERTDIR}/btc-solo.key systemctl restart nginx EOF chmod 700 /usr/local/sbin/btc-make-cert.sh # ensure sudoers.d exists mkdir -p /etc/sudoers.d chmod 750 /etc/sudoers.d # sudoers cat > /etc/sudoers.d/btc-solo <<'EOF' www-data ALL=(root) NOPASSWD: /usr/local/sbin/btc-apply-admin.sh www-data ALL=(root) NOPASSWD: /usr/local/sbin/btc-set-prune.sh www-data ALL=(root) NOPASSWD: /usr/local/sbin/btc-make-cert.sh www-data ALL=(root) NOPASSWD: /usr/local/sbin/btc-set-donation.sh EOF chmod 440 /etc/sudoers.d/btc-solo # --------------------------------------------------------------------------- echo "[6/8] Web UI (dashboard + activate)" # --------------------------------------------------------------------------- # web-owned state dir mkdir -p /var/lib/btc-solo chown www-data:www-data /var/lib/btc-solo # web dashboard mkdir -p "$WWW_ROOT" mkdir -p "$WWW_ROOT/activate" # main dashboard cat > "$WWW_ROOT/index.php" <<'EOF' '550 MB (default, small)', (2*1024) => '2 GB', (4*1024) => '4 GB', (8*1024) => '8 GB', (16*1024) => '16 GB', (32*1024) => '32 GB', (64*1024) => '64 GB', (100*1024) => '100 GB', (200*1024) => '200 GB', (300*1024) => '300 GB', (400*1024) => '400 GB', 'full' => 'Full (no pruning)', ]; $current = '550'; $conf = @file_get_contents('/etc/bitcoin/bitcoin.conf'); if ($conf && preg_match('/^prune=(\d+)/m', $conf, $m)) { $current = $m[1]; } elseif ($conf && !preg_match('/^prune=/m', $conf)) { $current = 'full'; } if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['prune'])) { $sel = $_POST['prune']; if (isset($opts[$sel])) { shell_exec('sudo /usr/local/sbin/btc-set-prune.sh ' . escapeshellarg($sel)); $current = $sel; sleep(1); } } $host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_ADDR'] ?? gethostname(); $host = preg_replace('/:\d+$/', '', $host); // strip port if present $stratum = 'stratum+tcp://' . $host . ':3333'; ?> Local Bitcoin Solo Mining

Local Bitcoin Solo Mining

Point your ASICs to this server and keep 100% of your block.

Stratum Endpoint

Username: your BTC address

Password: anything

Prune Size

Note: Prune size only limits stored block data. You should also expect the node to use up to 500 MB for chainstate and metadata. Actual disk usage will be higher than the value selected here.

Changing this will restart bitcoind.

Operator / Donation

Default is 2% to the project address. Set to 0 to keep the full block.

System

Check systemctl status bitcoind and systemctl status ckpool.

EOF # activation page cat > "$WWW_ROOT/activate/index.php" <<'EOF' Create New Admin User

Create New Admin User

This sets the Bitcoin node’s administrative (RPC) credentials used to control the server itself.

Keep these private — they grant full access to your node.

These credentials are not used by miners to connect; miners only need your Bitcoin address and can use any password.

EOF # --------------------------------------------------------------------------- echo "[7/8] NGINX with HTTPS + redirect" # --------------------------------------------------------------------------- mkdir -p "$CERT_DIR" openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \ -keyout ${CERT_DIR}/btc-solo.key \ -out ${CERT_DIR}/btc-solo.crt \ -subj "/C=CA/ST=Ontario/L=Barrie/O=BTC-Solo/OU=IT/CN=$(hostname -f)" >/dev/null 2>&1 chmod 600 ${CERT_DIR}/btc-solo.key cat > /etc/nginx/sites-available/btc-solo <