
How to create your own Certificate Authority to provide certs for your self-hosted services
Aug 28, 2025
9 min read
Some context
In the last years, my homelab has grown exponentially. I started with a Raspberry Pi zero back in University, just as a playground with nothing special in there, and nowadays it consists of a Synology NAS server, a four Raspberry PI 4 cluster, and a Proxmox cluster with two nodes backed by some Beelink Mini PCs. And of course, all of this hardware serves multiple self-hosted services that are currently very integrated in my daily workflow and other aspects of my life.
Most of my services are only accessible from my LAN, and I want them to stay like that. I can access them via their local IP 192.168.X.X
, or using a local domain name like myservice.local
. And of course, I would like to access them via secure HTTPS connections, just in case. I think nowadays is it possible to use auto-managed Let's encrypt certificates with those, either using Cloudflare tunneling, Tailscale, or other tools. But for me personally, using your own CA for self-signing certificates is much easier to setup, and also more fun. At the end of the day, those are local services on my homelab, so I don't need complex solutions, and I want to learn and experiment.
Why creating our own CA?
I also want, for the services I use the most, to keep things stable and avoid service interruptions, failures, and cumbersome manual tasks. When you generate a self-signed certificate, you must manually trust it in your client devices. What happens when you have N services? Without your own CA, you have N different certificates, and you must trust the N different certificates into your M different devices. And each time one of them expires and you generate a new one, you must trust it again.
This problem is already solved, that is one of the reasons why Certificate Authorities (CA) exist! If you create your own CA and use it to generate and sign all of your self-signed certificates, you only need to trust the CA, and that's it!
- So an existing certificate expires and you renew it? No problem! Your devices already trust it.
- So you need to add a new IP or a new domain name to an existing service certificate? No problem! You won't need to share and trust the new certificate elsewhere.
So, how do we achieve this?
Creating our own CA
We can create our own CA with openssl
from the terminal. The commands I share here works in UNIX like system like Linux and MacOS. I'm not sure if they will work as they are in Windows, but shouldn't be so much different, and the main concepts are the same.
First what comes first. Let's generate the private key for our CA. I recommend using a minimum length of 4096 bytes.
openssl genrsa -out rootCA.key 4096
Now, let's use it to generate the root certificate for our CA. I'll set an expiration date in ten years from now, but feel free to use whatever value you prefer.
openssl req -x509 -new -key rootCA.key -sha256 -days 3650 -out rootCA.pem
It will ask for the certificate data, I usually only fill the Country Name
(ES
in my case) and the Organization Name
and Common Name
with the same value of My own CA
.
Congratulations, now you have your own CA! From now on, we will use this newly generated root certificate and key to generate a sign any certificate we need for our homelab. Keep those safe and don't lose them! I usually store them as a .zip
into my 1Password vault, along with the rootCA.srl
file (we'll generate it later). You can use any password manager or store them as an encrypted .zip
file or whatever in any public cloud or hosting service you usually use.
Now, as explained earlier, you only need to trust the CA root certificate, and all certificates generated from it will be automatically trusted. Every device differs a little bit, but I'll show you next how to trust it on Apple devices.
Trusting the CA root certificate in iPhone, iPad and Mac
Trusting the CA root certificate in MacOS is fairly easy. Just double click on the rootCA.pem
file and the Keychain app will open. Important! Don't confuse it with the new Passwords app introduced in (I think) MacOS 15 Sequoia. Once on the Keychain app, look for it (it should appear under the Common Name
, in my case My own CA
), double click again, and in the window it opens select "Trust" > "When using this certificate" > "Trust always".
On iPhone and iPad it requires a little bit more of work. Currently tested on iOS and iPadOS versions 18. If in the future we have a new iPadOS more computer like things can differ a little. Anyway, just send the rootCA.pem
to your device via Airdrop or whatever you prefer, and open it. It will ask you where do you want to install it. Select "this iPhone" or "this iPad". Then, go to Settings and you'll see the new profile ready to install, just bellow your name from your Apple account. Click there, accept the warning about the cert being untrusty (we generated it) and install it. That's everything? Well, no. The cert is now installed, but Safari and other browsers like Chrome won't trust it yet. We need a last step. We must go to Settings > General > About > (scroll down to the end) Certificate trust settings. There, you will see our new installed root certificate and a deactivated toggle. Flip the toggle, accept the new warning, and voilà.
Requesting the CA a new certificate for our service
To generate certificates for our services, we need to create a Certificate Signing Request (CSR) for them. Think about it like the CA is the Government, the service is you, and you fill a form (the CSR) to request anything. Like any request, as you will do with your Government, you need to sign it. So first, we need to generate a private key for our service/server. This is the same as we did initially for the CA key.
openssl genrsa -out server.key 4096
Now, we create and sign with the server key the CSR for our CA.
openssl req -new -key server.key -out server.csr
Again, it will ask you some basic data for the future certificate. Like before, I just set the Country Name
, Organization Name
(in my case to My Homelab
) and Common Name
(in my case, to the service or server name, like My Service
). The rest of fields, including challenge password, are left empty.
Using the CA to generate a new certificate from the CSR
For better security and improved support, we'll use an X.509 extensions file to define some parameters for the new certificate to be generated. Create a file v3.ext
and update the values under [alt_names]
with your own:
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = myservice.local
IP.1 = 192.168.1.123
Is it possible to include more DNS names or IPs. Just add extra entries like DNS.2
or IP.2
and so on. Also, it's possible to use wildcards to generate a certificate valid for all subdomains, like DNS.2 = *.myserver.local
. If you use wildcards, ensure you are using it under the DNS.2
entry, and leave the DNS.1
for the root domain like DNS.1 = myserver.local
.
Next, let's use our CA to generate a new certificate from the CSR, using the above extensions file. I will use SHA-256 to sign it and set a validity period of 825 days, as apple won't trust certificates longer than that, and me and my family use Apple devices. Feel free to set a longer validity period (like another 10 years) if this doesn't care to you.
openssl x509 -req -in server.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial \
-out server.crt -days 825 -sha256 -extfile v3.ext
Now you will have two new files in your directory:
server.crt
is the new certificate for your service/server that you can use along with the earlier generatedserver.key
. Just upload them to your server or service and configure it to start using them.rootCA.srl
stores the next serial number your CA will assign when signing a new certificate. Each time you sign a certificate with your CA, OpenSSL reads and updates this file, ensuring that every certificate has a unique serial number (which is required for security and validity). **You must keep thisrootCA.srl
in the same directory as yourrootCA.key
androotCA.pem
every time you generate a new certificate with this CA. Deleting or losing this file can cause duplicate serial numbers, which can invalidate your CA or cause certificate management issues.
Renewing server certificates
To renew a server certificates, for example because it's near its expiry date, you will use a CSR again. You could theoretically reuse the same CSR you used for your initial certificate, and or the same server private key, but it's better for security to generate a new private key for the server and use it to create a new Certificate Signing Request.
openssl genrsa -out server.key 4096
openssl req -new -key server.key -out server.csr
And then, sign it with the CA. This time, we do not use the -CAcreateserial
option and instead relies on the existing rootCA.srl
file with -CAserial
.
openssl x509 -req -in server.csr -CA rootCA.pem -CAkey rootCA.key \
-CAserial rootCA.srl -out server.crt -days 825 -sha256 -extfile v3.ext
Renewing CA certificates
And what about renewing the CA certificates without invalidating previous generated certs using it? If we want all existing certs to remain trusted, we just need to generate a new CA root certificate using the same private key we used the first time. In fact, the command is exactly the same:
openssl req -x509 -new -key rootCA.key -sha256 -days 3650 -out rootCA.pem"
And that's it. Simple, and you keep full control. The only cons if that you need to remember to update the certs before they expiring. It's a good idea to write you down a reminder somewhere that will notify you in time (I write myself a reminder in Todoist to notify 30 days prior expiration). If you want, you could also automatize it, by writing an small service, CLI or API that will receive a CSR and will provide new certs for you. Kinda like how Let's Encrypt works, but homemade.
I hope my article has helped you, or at least, that you have enjoyed reading it. I do this for fun and I don't need money to keep the blog running. However, if you'd like to show your gratitude, you can pay for my next coffee with a one-time donation of just $1.00. Thanks!
Pay my next coffee
