diff --git a/doc/guix.texi b/doc/guix.texi
index 751d0957d8..62fefde1ea 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -111,6 +111,7 @@ Copyright @copyright{} 2022 (@*
 Copyright @copyright{} 2022 John Kehayias@*
 Copyright @copyright{} 2022 Ivan Vilata-i-Balaguer@*
 Copyright @copyright{} 2023 Giacomo Leidi@*
+Copyright @copyright{} 2023 David Wilson@*

 Permission is granted to copy, distribute and/or modify this document
 under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -41061,6 +41062,7 @@ services)}.
 * Shepherd: Shepherd Home Service.                     Managing User's Daemons.
 * SSH: Secure Shell.                                   Setting up the secure shell client.
 * Desktop: Desktop Home Services.                      Services for graphical environments.
+* Emacs: Emacs Home Services.                          Services for configuring Emacs.
 * Guix: Guix Home Services.                            Services for Guix.
 @end menu
 @c In addition to that Home Services can provide
@@ -41914,6 +41916,81 @@ The package providing the @code{/bin/dbus-daemon} command.
 @end table
 @end deftp

+@node Emacs Home Services
+@subsection Emacs Home Services
+
+@defvr {Scheme Variable} home-emacs-service-type
+This is the service type for configuring the Emacs text editor.  It
+enables you to assemble @file{init.el} and @file{early-init.el} files
+from snippets in your home configuration and other Emacs Lisp files you
+have in your personal configuration folder.
+
+This service can be extended using the @code{home-emacs-extension} type.
+
+Note that if you have an existing @file{~/.emacs} and/or
+@file{~/.emacs.d}, the configuration aspect of this service will not be
+loaded, as the former location takes precedence over
+@file{~/.config/emacs}.  This service uses the latter path in the
+interest of cleanliness.  To migrate to the XDG directory, run these
+commands:
+
+@example
+$ cp ~/.emacs.d $XDG_CONFIG_HOME/emacs
+$ cp ~/.emacs $XDG_CONFIG_HOME/emacs/init.el
+@end example
+@end defvr
+
+@deftp {Data Type} home-emacs-configuration
+The configuration record for @code{home-emacs-service-type}.
+
+@table @asis
+@item @code{emacs} (default: @code{emacs})
+The package providing the @file{/bin/emacs} command.
+
+@item @code{packages} (default: @code{'()})
+Additional packages required by the Emacs configuration.
+
+@item @code{user-emacs-directory} (default: @file{~/.cache/emacs})
+The directory beneath which additional per-user Emacs-specific files are placed.
+
+@item @code{init-file} (default: @code{'()})
+Configuration text or files to include in @file{init.el}.
+
+@item @code{early-init-file} (default: @code{'()})
+Configuration text or files to include in @file{early-init.el}.
+
+@item @code{load-paths} (default: @code{'()})
+Additional load paths to add to Emacs' @code{load-path} variable.  Lines
+will be inserted at the beginning of @file{early-init.el}.
+
+@item @code{native-compile?} (default: @code{#f})
+Whether to compile all @code{packages}, using the provided @code{emacs}
+package in place of @code{emacs-minimal}, which will enable native
+compilation if the @code{emacs} package supports it.  All
+non-@code{-minimal} Emacs packages at version 28 or above should support
+native compilation.
+@end table
+@end deftp
+
+@deftp {Data Type} home-emacs-extension
+The extension record for @code{home-emacs-service-type}.
+
+@table @asis
+@item @code{packages} (default: @code{'()})
+Additional packages required by the Emacs configuration.
+
+@item @code{init-file} (default: @code{'()})
+Configuration text or files to include in @file{init.el}.
+
+@item @code{early-init-file} (default: @code{'()})
+Configuration text or files to include in @file{early-init.el}.
+
+@item @code{load-paths} (default: @code{'()})
+Additional load paths to add to Emacs' @code{load-path} variable.  Lines
+will be inserted at the beginning of @file{early-init.el}.
+@end table
+@end deftp
+
 @node Guix Home Services
 @subsection Guix Home Services

diff --git a/gnu/home/services/emacs.scm b/gnu/home/services/emacs.scm
new file mode 100644
index 0000000000..45b4f2a5d6
--- /dev/null
+++ b/gnu/home/services/emacs.scm
@@ -0,0 +1,188 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2022 ( <paren@disroot.org>
+;;; Copyright © 2023 David Wilson <david@daviwil.com>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu home services emacs)
+  #:use-module (gnu home services)
+  #:autoload   (gnu packages emacs) (emacs-minimal
+                                     emacs)
+  #:use-module (gnu services configuration)
+  #:use-module (guix gexp)
+  #:use-module (guix packages)
+  #:use-module (guix records)
+  #:use-module (ice-9 match)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-26)
+
+  #:export (emacs-variables
+            home-emacs-configuration
+            home-emacs-extension
+            home-emacs-service-type))
+
+(define list-of-file-likes?
+  (list-of file-like?))
+
+(define (string-or-file-like? val)
+  (or (string? val)
+      (file-like? val)))
+
+(define list-of-string-or-file-likes?
+  (list-of string-or-file-like?))
+
+(define-configuration/no-serialization home-emacs-configuration
+  (emacs
+   (file-like emacs)
+   "The package providing the @file{/bin/emacs} command.")
+  (packages
+   (list-of-file-likes '())
+   "Additional packages required by the Emacs configuration.")
+  (user-emacs-directory
+   (string "~/.cache/emacs")
+   "Directory beneath which additional per-user Emacs-specific files are placed.")
+  (init-file
+   (text-config '())
+   "Configuration text or files to include in init.el.")
+  (early-init-file
+   (text-config '())
+   "Configuration text or files to include in early-init.el.")
+  (load-paths
+   (list-of-string-or-file-likes '())
+   "Additional load paths to add to Emacs' @code{load-path} variable.  Lines
+will be inserted at the beginning of early-init.el.")
+  (native-compile?
+   (boolean #f)
+   "Whether to compile the @code{packages} using the Emacs package
+provided as the value of the @code{emacs} field, which will enable
+native compilation if the @code{emacs} package supports it."))
+
+(define (home-emacs-profile-packages config)
+  (cons (home-emacs-configuration-emacs config)
+        (home-emacs-configuration-packages config)))
+
+(define (home-emacs-transformed-packages config)
+  (map (if (home-emacs-configuration-native-compile? config)
+           (package-input-rewriting
+            `((,emacs-minimal
+              . ,(home-emacs-configuration-emacs config))))
+           identity)
+       (let ((packages (home-emacs-configuration-packages config)))
+         (concatenate
+          (cons packages
+                (map (compose (cute map second <>)
+                              package-transitive-propagated-inputs)
+                     packages))))))
+
+(define (serialize-emacs-load-paths config)
+  #~(string-append
+     ";; Additional load paths\n"
+     #$@(map (lambda (load-path)
+               #~(format #f "(add-to-list 'load-path \"~a\")" #$load-path))
+             (home-emacs-configuration-load-paths config))
+     "\n\n"))
+
+(define (serialize-emacs-user-directory config)
+  (format #f
+          ";; Set the `user-emacs-directory` to a writeable path\n(setq user-emacs-directory \"~a\")\n\n"
+          (home-emacs-configuration-user-emacs-directory config)))
+
+(define (home-emacs-xdg-configuration-files config)
+  `(("emacs/early-init.el"
+     ,(apply mixed-text-file
+             (cons* "early-init.el"
+                    (serialize-emacs-load-paths config)
+                    (serialize-emacs-user-directory config)
+                    (home-emacs-configuration-early-init-file config))))
+    ("emacs/init.el"
+     ,(apply mixed-text-file
+             (cons "init.el"
+                   (home-emacs-configuration-init-file config))))))
+
+(define-configuration/no-serialization home-emacs-extension
+  (packages
+   (list-of-file-likes '())
+   "Additional packages required by the Emacs configuration.")
+  (init-file
+   (text-config '())
+   "Configuration text or files to include in init.el.")
+  (early-init-file
+   (text-config '())
+   "Configuration text or files to include in early-init.el.")
+  (load-paths
+   (list-of-string-or-file-likes '())
+   "Additional load paths to add to Emacs' @code{load-path} variable.  Lines
+will be inserted at the beginning of early-init.el."))
+
+(define (home-emacs-extensions original-config extension-configs)
+  (match-record original-config <home-emacs-configuration>
+    (packages load-paths init-file early-init-file)
+    (home-emacs-configuration
+     (inherit original-config)
+     (packages
+      (append packages
+              (append-map
+               home-emacs-extension-packages extension-configs)))
+     (init-file
+      (append init-file
+              (append-map
+               home-emacs-extension-init-file extension-configs)))
+     (early-init-file
+      (append early-init-file
+              (append-map
+               home-emacs-extension-early-init-file extension-configs)))
+     (load-paths
+      (append load-paths
+              (append-map
+               home-emacs-extension-load-paths extension-configs))))))
+
+(define home-emacs-service-type
+  (service-type
+   (name 'home-emacs)
+   (extensions
+    (list (service-extension
+           home-profile-service-type
+           home-emacs-profile-packages)
+          (service-extension
+           home-shepherd-service-type
+           home-emacs-shepherd-services)
+          (service-extension
+           home-xdg-configuration-files-service-type
+           home-emacs-xdg-configuration-files)))
+   (default-value (home-emacs-configuration))
+   (compose identity)
+   (extend home-emacs-extensions)
+   (description
+    "Configure the GNU Emacs extensible text editor.")))
+
+(define scheme-value->emacs-value
+  (match-lambda (#t (quote 't))
+                (#f (quote 'nil))
+                (val val)))
+
+(define (emacs-variables var-alist)
+  "Converts an alist of variable names and values into a @code{setq}
+expression that can be used in an Emacs configuration.  Scheme values
+@code{#t} and @code{#f} will be converted into @code{t} and @code{nil},
+respectively."
+  #~(string-append
+     "(setq"
+     #$@(map (lambda (var)
+               #~(format #f "\n  ~a ~s"
+                         (quote #$(car var))
+                         #$(scheme-value->emacs-value (cdr var))))
+             var-alist)
+     ")\n\n"))
diff --git a/gnu/local.mk b/gnu/local.mk
index 184f43e753..35d88b4dd6 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -89,6 +89,7 @@ GNU_SYSTEM_MODULES =				\
   %D%/home/services.scm			\
   %D%/home/services/desktop.scm			\
   %D%/home/services/symlink-manager.scm		\
+  %D%/home/services/emacs.scm			\
   %D%/home/services/fontutils.scm		\
   %D%/home/services/guix.scm			\
   %D%/home/services/pm.scm			\
