diff --git a/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixos/modules/services/monitoring/prometheus/exporters.nix
index 0b65b4865b15..e0c5ceccfcc9 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters.nix
@@ -25,6 +25,7 @@ let
     "artifactory"
     "bind"
     "bird"
+    "bitcoin"
     "blackbox"
     "collectd"
     "dnsmasq"
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/bitcoin.nix b/nixos/modules/services/monitoring/prometheus/exporters/bitcoin.nix
new file mode 100644
index 000000000000..43721f70b499
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/bitcoin.nix
@@ -0,0 +1,82 @@
+{ config, lib, pkgs, options }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.bitcoin;
+in
+{
+  port = 9332;
+  extraOpts = {
+    rpcUser = mkOption {
+      type = types.str;
+      default = "bitcoinrpc";
+      description = ''
+        RPC user name.
+      '';
+    };
+
+    rpcPasswordFile = mkOption {
+      type = types.path;
+      description = ''
+        File containing RPC password.
+      '';
+    };
+
+    rpcScheme = mkOption {
+      type = types.enum [ "http" "https" ];
+      default = "http";
+      description = ''
+        Whether to connect to bitcoind over http or https.
+      '';
+    };
+
+    rpcHost = mkOption {
+      type = types.str;
+      default = "localhost";
+      description = ''
+        RPC host.
+      '';
+    };
+
+    rpcPort = mkOption {
+      type = types.port;
+      default = 8332;
+      description = ''
+        RPC port number.
+      '';
+    };
+
+    refreshSeconds = mkOption {
+      type = types.ints.unsigned;
+      default = 300;
+      description = ''
+        How often to ask bitcoind for metrics.
+      '';
+    };
+
+    extraEnv = mkOption {
+      type = types.attrsOf types.str;
+      default = {};
+      description = ''
+        Extra environment variables for the exporter.
+      '';
+    };
+  };
+  serviceOpts = {
+    script = ''
+      export BITCOIN_RPC_PASSWORD=$(cat ${cfg.rpcPasswordFile})
+      exec ${pkgs.prometheus-bitcoin-exporter}/bin/bitcoind-monitor.py
+    '';
+
+    environment = {
+      BITCOIN_RPC_USER = cfg.rpcUser;
+      BITCOIN_RPC_SCHEME = cfg.rpcScheme;
+      BITCOIN_RPC_HOST = cfg.rpcHost;
+      BITCOIN_RPC_PORT = toString cfg.rpcPort;
+      METRICS_ADDR = cfg.listenAddress;
+      METRICS_PORT = toString cfg.port;
+      REFRESH_SECONDS = toString cfg.refreshSeconds;
+    } // cfg.extraEnv;
+  };
+}
diff --git a/nixos/tests/prometheus-exporters.nix b/nixos/tests/prometheus-exporters.nix
index a43dca4fe14d..62c0080dd516 100644
--- a/nixos/tests/prometheus-exporters.nix
+++ b/nixos/tests/prometheus-exporters.nix
@@ -136,6 +136,24 @@ let
       '';
     };
 
+    bitcoin = {
+      exporterConfig = {
+        enable = true;
+        rpcUser = "bitcoinrpc";
+        rpcPasswordFile = pkgs.writeText "password" "hunter2";
+      };
+      metricProvider = {
+        services.bitcoind.default.enable = true;
+        services.bitcoind.default.rpc.users.bitcoinrpc.passwordHMAC = "e8fe33f797e698ac258c16c8d7aadfbe$872bdb8f4d787367c26bcfd75e6c23c4f19d44a69f5d1ad329e5adf3f82710f7";
+      };
+      exporterTest = ''
+        wait_for_unit("prometheus-bitcoin-exporter.service")
+        wait_for_unit("bitcoind-default.service")
+        wait_for_open_port(9332)
+        succeed("curl -sSf http://localhost:9332/metrics | grep -q '^bitcoin_blocks '")
+      '';
+    };
+
     blackbox = {
       exporterConfig = {
         enable = true;
diff --git a/pkgs/development/python-modules/riprova/default.nix b/pkgs/development/python-modules/riprova/default.nix
new file mode 100644
index 000000000000..adf327208955
--- /dev/null
+++ b/pkgs/development/python-modules/riprova/default.nix
@@ -0,0 +1,29 @@
+{ lib
+, buildPythonPackage
+, fetchPypi
+, six
+}:
+
+buildPythonPackage rec{
+  pname = "riprova";
+  version = "0.2.7";
+
+  src = fetchPypi {
+    inherit pname version;
+    sha256 = "04drdvjjbh370csv2vb5zamg2aanxqkfm6w361qkybnra4g4g0dz";
+  };
+
+  propagatedBuildInputs = [ six ];
+
+  # PyPI archive doesn't have tests
+  doCheck = false;
+
+  pythonImportsCheck = [ "riprova" ];
+
+  meta = with lib; {
+    homepage = "https://github.com/h2non/riprova";
+    description = "Small and versatile library to retry failed operations using different backoff strategies";
+    license = licenses.mit;
+    maintainers = with maintainers; [ mmilata ];
+  };
+}
diff --git a/pkgs/servers/monitoring/prometheus/bitcoin-exporter.nix b/pkgs/servers/monitoring/prometheus/bitcoin-exporter.nix
new file mode 100644
index 000000000000..333be3732112
--- /dev/null
+++ b/pkgs/servers/monitoring/prometheus/bitcoin-exporter.nix
@@ -0,0 +1,42 @@
+{ lib, fetchFromGitHub, fetchpatch, python3Packages }:
+
+python3Packages.buildPythonApplication rec {
+  pname = "bitcoin-prometheus-exporter";
+  version = "0.5.0";
+
+  format = "other";
+
+  src = fetchFromGitHub {
+    owner = "jvstein";
+    repo = pname;
+    rev = "v${version}";
+    sha256 = "0l0j6dyb0vflh386z3g8srysay5sf47g5rg2f5xrkckv86rjr115";
+  };
+
+  patches = [
+    # remove after update to new release
+    (fetchpatch {
+      name = "configurable-listening-address.patch";
+      url = "https://patch-diff.githubusercontent.com/raw/jvstein/bitcoin-prometheus-exporter/pull/11.patch";
+      sha256 = "0a2l8aqgprc1d5k8yg1gisn6imh9hzg6j0irid3pjvp5i5dcnhyq";
+    })
+  ];
+
+  propagatedBuildInputs = with python3Packages; [ prometheus_client bitcoinlib riprova ];
+
+  installPhase = ''
+    mkdir -p $out/bin
+    cp bitcoind-monitor.py $out/bin/
+
+    mkdir -p $out/share/${pname}
+    cp -r dashboard README.md $out/share/${pname}/
+  '';
+
+  meta = with lib; {
+    description = "Prometheus exporter for Bitcoin Core nodes";
+    homepage = "https://github.com/jvstein/bitcoin-prometheus-exporter";
+    license = licenses.bsd3;
+    maintainers = with maintainers; [ mmilata ];
+    platforms = platforms.all;
+  };
+}
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
index 56e4e86a576d..3870915745e4 100644
--- a/pkgs/top-level/all-packages.nix
+++ b/pkgs/top-level/all-packages.nix
@@ -18876,6 +18876,7 @@ in
   prometheus-aws-s3-exporter = callPackage ../servers/monitoring/prometheus/aws-s3-exporter.nix { };
   prometheus-bind-exporter = callPackage ../servers/monitoring/prometheus/bind-exporter.nix { };
   prometheus-bird-exporter = callPackage ../servers/monitoring/prometheus/bird-exporter.nix { };
+  prometheus-bitcoin-exporter = callPackage ../servers/monitoring/prometheus/bitcoin-exporter.nix { };
   prometheus-blackbox-exporter = callPackage ../servers/monitoring/prometheus/blackbox-exporter.nix { };
   prometheus-collectd-exporter = callPackage ../servers/monitoring/prometheus/collectd-exporter.nix { };
   prometheus-consul-exporter = callPackage ../servers/monitoring/prometheus/consul-exporter.nix { };
diff --git a/pkgs/top-level/python-packages.nix b/pkgs/top-level/python-packages.nix
index f84848c072c0..ae06f4348b31 100644
--- a/pkgs/top-level/python-packages.nix
+++ b/pkgs/top-level/python-packages.nix
@@ -7282,6 +7282,8 @@ in {
 
   ring-doorbell = callPackage ../development/python-modules/ring-doorbell { };
 
+  riprova = callPackage ../development/python-modules/riprova { };
+
   ripser = callPackage ../development/python-modules/ripser { };
 
   rising = callPackage ../development/python-modules/rising { };