# How do you manage custom CA certificates?

**URL:** <https://forum.systemcrafters.net/t/how-do-you-manage-custom-ca-certificates/1929>\
**Category:** Guix\
**Created:** [March 24, 2026, 10:31am UTC](https://forum.systemcrafters.net/t/how-do-you-manage-custom-ca-certificates/1929 "2026-03-24T10:31:01Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![GunnarGrop](https://yyz1.discourse-cdn.com/flex029/user_avatar/forum.systemcrafters.net/gunnargrop/32/1196_2.png) [@GunnarGrop](https://forum.systemcrafters.net/u/GunnarGrop)\
**Post date:** [March 24, 2026, 10:31am UTC](https://forum.systemcrafters.net/t/how-do-you-manage-custom-ca-certificates/1929/1 "2026-03-24T10:31:01Z")

</div>

I started using Guix System a few weeks ago, and thus far I’m really impressed. There’s just one thing I just haven’t been able to wrap my head around: how do I trust custom CA certificates?

I read through the Guix [documentation on X.509 Certificates](https://guix.gnu.org/manual/1.5.0/en/html_node/X_002e509-Certificates.html) but I feel like it isn’t very through.

I have a PEM encoded CA certificate that I want to trust (preferably on the entire system) so that I can curl/wget/whatever without having to specify any environment variables or flags. I guess I should have to modify the nss-certs package to do this?

How do you handle this? I’d love to know.

---

<div class="post-metadata">

**Author:** ![wxie](https://avatars.discourse-cdn.com/v4/letter/w/ee59a6/32.png) [@wxie](https://forum.systemcrafters.net/u/wxie)\
**Post date:** [March 25, 2026, 12:12am UTC](https://forum.systemcrafters.net/t/how-do-you-manage-custom-ca-certificates/1929/2 "2026-03-25T00:12:26Z")

</div>

You may need to write a service for your certificate, e.g. to extend the nss-certs.

---

<div class="post-metadata">

**Author:** ![spk44](https://avatars.discourse-cdn.com/v4/letter/s/c4cdca/32.png) [@spk44](https://forum.systemcrafters.net/u/spk44)\
**Post date:** [April 23, 2026, 2:01pm UTC](https://forum.systemcrafters.net/t/how-do-you-manage-custom-ca-certificates/1929/3 "2026-04-23T14:01:58Z")

</div>

I’ve created a package to install custom certificates system-wide. I first [created my own channel](https://guix.gnu.org/manual/1.5.0/en/html_node/Creating-a-Channel.html) to host the package definition. It has the following directory structure,

> vec  
> ├── packages  
> │ ├── aux-files  
> │ │ ├── Issuing-CA3.1.pem  
> │ │ └── Root\_CA\_2.0.pem  
> │ └── custom-certs.scm  
> └── packages.scm

The top level directory name was chosen arbitrarily, but the rest follow Guix system conventions. Inside the `packages.scm` file is just a helper function,

```scheme
(define-module (vec packages)
  #:use-module (guix packages)
  #:use-module (guix diagnostics)
  #:use-module (guix i18n)
  #:use-module (srfi srfi-26)
  #:export (search-auxiliary-file
            %auxiliary-files-path))

(define %auxiliary-files-path
  (make-parameter
   (map (cut string-append <> "/vec/packages/aux-files")
        %load-path)))

(define (search-auxiliary-file file-name)
  "Search the auxiliary FILE-NAME. Return #f if not found."
  (search-path (%auxiliary-files-path) file-name))

```

and inside `custom-certs.scm` is the package definition.

```scheme
(define-module (vec packages custom-certs)
  #:use-module (guix gexp)
  #:use-module (guix packages)
  #:use-module (guix build-system trivial)
  #:use-module ((guix licenses) #:prefix license:)
  #:use-module (gnu packages perl)
  #:use-module (gnu packages tls)
  #:use-module (vec packages))

(define nonfree(
  (@@ (guix licenses) license) "Nonfree"
           "."
           "This a nonfree license."))

(define-public custom-certs
  (package
    (name "custom-certs")
    (version "3.1")
    (source #f)
    (build-system trivial-build-system)
    (arguments
     '(#:modules ((guix build utils))
       #:builder
       (begin
         (use-modules (guix build utils))
         (let ((root-cert (assoc-ref %build-inputs "root-cert"))
               (issuing-cert (assoc-ref %build-inputs "issuing-cert"))
               (out (string-append (assoc-ref %outputs "out") "/etc/ssl/certs"))
               (openssl (assoc-ref %build-inputs "openssl"))
               (perl (assoc-ref %build-inputs "perl")))
           (mkdir-p out)
           (for-each
             (lambda (cert)
               (copy-file cert (string-append out "/" (strip-store-file-name cert))))
             (list root-cert issuing-cert))

           ;; Create hash symlinks suitable for OpenSSL ('SSL_CERT_DIR' and
           ;; similar.)
           (chdir (string-append %output "/etc/ssl/certs"))
           (invoke (string-append perl "/bin/perl")
                   (string-append openssl "/bin/c_rehash")
                   ".")))))
    (native-inputs
      (list openssl perl)) ;for 'c_rehash'
    (inputs
      `(("root-cert", (search-auxiliary-file "Root_CA_2.0.pem"))
        ("issuing-cert", (search-auxiliary-file "Issuing-CA3.1.pem"))))
    (synopsis "Custome Certificates")
    (description "This package provides certificates for an internal network.")
    (home-page "https://www.example.com")
    (license nonfree)))

```

I marked this as non-free since it is for my company’s internal network, choose whichever license you want for yours. Also, the certs must be in `.pem` format for Guix’s bundling step to work.

After [adding the channel](https://guix.gnu.org/manual/1.5.0/en/html_node/Specifying-Additional-Channels.html), this package can be included to your system config, or user package list just like any other package.

---

<div class="post-metadata">

**Author:** ![aionfork](https://yyz1.discourse-cdn.com/flex029/user_avatar/forum.systemcrafters.net/aionfork/32/1243_2.png) [@aionfork](https://forum.systemcrafters.net/u/aionfork)\
**Post date:** [April 26, 2026, 7:26am UTC](https://forum.systemcrafters.net/t/how-do-you-manage-custom-ca-certificates/1929/4 "2026-04-26T07:26:29Z")

</div>

See [`nss-certs` and `nss-certs-for-test` in ./gnu/packages/nss.scm](https://codeberg.org/guix/guix/src/branch/master/gnu/packages/nss.scm#L317-L396)… or just run `guix edit nss-certs` to jump to it with `$EDITOR`.

the `nss-certs` package inherits its source from `nss`. it gets pulled from `https://ftp.mozilla.org/pub/mozilla.org/security/nss/...` and is validated by hash.

@spk44’s suggestion is in the right direction. Some versioning on the `(source ...)` that the `.pem` files are fetched from would be essential later on.

I think the files just need to be in `/etc/ssl/certs` and `$SSL_CERT_*` vars need to be get set in the system profile. idk i could be wrong.

```scheme
;; maybe not typically necessary
(native-search-paths
      (list $SSL_CERT_DIR
            $SSL_CERT_FILE))

```

idk authoritatively. it depends on:

- how you want to inject the trust: system-wide, for a user’s profile, for an application/browser/container.
- whether you care about certificate bundles ending up in `/gnu/store` on build machines
- how you want to manage/rotate/revoke trust later. in some cases, resetting trust could require restarting services/apps.

in the past, i think i tried using the [extra-special-file](https://guix.gnu.org/manual/devel/en/guix.html#index-extra_002dspecial_002dfile) which works for a personal system definition… but probably a bad approach.

for managing private x509 trust, i imagine this beats ansible & other config tools by a pretty wide margin.

For actual secrets management, it would be worth checking out `sops-nix`, [EmergentMind/nix-config](https://github.com/EmergentMind/nix-config) and [EmergentMind/nix-secrets-reference](https://github.com/EmergentMind/nix-secrets-reference).

There’s also [fishinthecalculator/sops-guix](https://github.com/fishinthecalculator/sops-guix), which i had always wanted to use.

---

<div class="post-metadata">

**Author:** ![GunnarGrop](https://yyz1.discourse-cdn.com/flex029/user_avatar/forum.systemcrafters.net/gunnargrop/32/1196_2.png) [@GunnarGrop](https://forum.systemcrafters.net/u/GunnarGrop)\
**Post date:** [August 15, 2026, 9:55am UTC](https://forum.systemcrafters.net/t/how-do-you-manage-custom-ca-certificates/1929/5 "2026-08-15T09:55:11Z")

</div>

Sorry for the late reply, it took me a while to figure it out, but your solution was the “proper” way to do it. This was after looking at the _le-certs_ package from (gnu packages certs), which did it basically the same way you did. Thank you very much!

Also thanks to @aionfork for you input.

I’m glad this thread now exists, but I also created a short blog post where hopefully some poor soul will find guidance in the future. 🙂

> **[grop.dev - Trusting CA certificates in Guix System](https://grop.dev/posts/trusting-ca-certificates-in-guix-system/)**
