diff --git a/gnu/local.mk b/gnu/local.mk
index 855f2acccc..6ca7bf68ac 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -557,6 +557,7 @@ GNU_SYSTEM_MODULES =				\
   %D%/packages/polkit.scm			\
   %D%/packages/popt.scm				\
   %D%/packages/potassco.scm				\
+  %D%/packages/power.scm			\
   %D%/packages/printers.scm			\
   %D%/packages/profiling.scm			\
   %D%/packages/prolog.scm			\
diff --git a/gnu/packages/power.scm b/gnu/packages/power.scm
new file mode 100644
index 0000000000..98dc98c6e4
--- /dev/null
+++ b/gnu/packages/power.scm
@@ -0,0 +1,125 @@
+;;; Copyright (C) 2025 Tomas Volf <~@wolfsden.cz>
+;;; Copyright (C) 2023 Raven Hallsby <karl@hallsby.com>
+
+;;;; Commentary:
+
+;;; Power-related packages.
+
+;;;; Code:
+
+(define-module (gnu packages power)
+  #:use-module (gnu)
+  #:use-module (gnu packages libusb)
+  #:use-module (gnu packages linux)
+  #:use-module (gnu packages pkg-config)
+  #:use-module (gnu packages python-xyz)
+  #:use-module (guix build-system gnu)
+  #:use-module (guix download)
+  #:use-module (guix gexp)
+  #:use-module ((guix licenses) #:prefix license:)
+  #:use-module (guix packages))
+
+(define-public apcupsd
+  (package
+    (name "apcupsd")
+    (version "3.14.14")
+    (source (origin
+              (method url-fetch)
+              (uri
+               (string-append
+                "mirror://sourceforge/" name "/" name " - Stable/" version
+                "/" name "-" version ".tar.gz"))
+              (sha256
+               (base32
+                "0rwqiyzlg9p0szf3x6q1ppvrw6f6dbpn2rc5z623fk3bkdalhxyv"))))
+    (native-inputs
+     (list pkg-config python-docutils))
+    (inputs
+     (list libusb libusb-compat))
+    (outputs '("out" "doc"))
+    (build-system gnu-build-system)
+    (arguments
+     (list
+      #:configure-flags
+      #~(list
+         ;; The configure script ignores --prefix for most of the paths.
+         (string-append "--exec-prefix=" #$output)
+         (string-append "--mandir=" #$output "/share/man")
+         (string-append "--sbindir=" #$output "/sbin")
+         (string-append "--sysconfdir=" #$output "/etc/apcupsd")
+         (string-append "--with-halpolicydir=" #$output "/share/halpolicy")
+
+         ;; Put us into the version string.
+         "--with-distname=GNU/Guix"
+         "--disable-install-distdir"
+
+         ;; State directories.
+         "--localstatedir=/var"
+         "--with-log-dir=/var/log"
+         "--with-pid-dir=/var/run"
+         "--with-lock-dir=/var/run/apcupsd/lock"
+         "--with-nologin=/var/run/apcupsd"
+         "--with-pwrfail-dir=/var/run/apcupsd"
+
+         ;; Configure requires these, but we do not use the genenerated
+         ;; apcupsd.conf, so in order to reduce dependencies of the package,
+         ;; provide fake values.
+         (string-append "ac_cv_path_SHUTDOWN=/nope")
+         (string-append "ac_cv_path_APCUPSD_MAIL=/nope")
+         ;; While `wall' is not expanded anywhere, it still is searched for.
+         (string-append "ac_cv_path_WALL=/nope")
+
+         ;; Enable additional drivers.
+         "--enable-test"
+         "--enable-usb"
+         "--enable-modbus-usb")
+      #:tests? #f                       ; There are no tests.
+      #:modules (cons '(ice-9 ftw) %default-gnu-modules)
+      #:phases
+      #~(modify-phases %standard-phases
+          ;; These are not installed, so trick Make into thinking they were
+          ;; already generated.  Allows us not to depend on mandoc, util-linux.
+          (add-before 'configure 'touch-txt-docs
+            (lambda _
+              (for-each (lambda (f)
+                          (call-with-output-file f close-port))
+                        '("doc/apcupsd.man.txt"
+                          "doc/apcaccess.man.txt"
+                          "doc/apctest.man.txt"
+                          "doc/apccontrol.man.txt"
+                          "doc/apcupsd.conf.man.txt"))))
+          (add-after 'build 'build-manual
+            (lambda _
+              (invoke "make" "-C" "doc/manual" "manual.html")))
+          (add-after 'install-license-files 'move-doc
+            (lambda _
+              (let ((target (string-append #$output:doc
+                                           "/share/doc/"
+                                           (strip-store-file-name #$output))))
+                (mkdir-p target)
+                (for-each (lambda (f)
+                            (copy-file (string-append "doc/manual/" f)
+                                       (string-append target "/" f)))
+                          (scandir "doc/manual"
+                                   (lambda (f)
+                                     (or (string-suffix? ".png" f)
+                                         (string-suffix? ".html" f))))))))
+          ;; If sending mails is required, use proper mail program.
+          (add-after 'install 'remove-smtp
+            (lambda _
+              (delete-file (string-append #$output "/sbin/smtp"))))
+          ;; The configuration files and scripts are not really suitable for
+          ;; Guix, and our service provides its own version anyway.  So nuke
+          ;; these to make sure `apcupsd' and `apctest' executed without any
+          ;; arguments fail.  `apctest' actually segfaults, but only after
+          ;; printing an error ¯\_(ツ)_/¯.
+          (add-after 'install 'remove-etc-apcupsd
+            (lambda _
+              (delete-file-recursively (string-append #$output "/etc/apcupsd")))))))
+    (home-page "https://www.apcupsd.org")
+    (synopsis "A daemon for controlling APC UPSes")
+    (description "Apcupsd can be used for power mangement and controlling most
+of APC’s UPS models on Unix and Windows machines.  Apcupsd works with most of
+APC’s Smart-UPS models as well as most simple signalling models such a Back-UPS,
+and BackUPS-Office.")
+    (license license:gpl2)))
