<p>Don't read the text written on Certbot site, the real talk is <a href="https://certbot.eff.org/docs/using.html?highlight=hooks#webroot" rel="nofollow noreferrer">in the documentation</a>.</p>
<p>In order to make less actions, consider grouping configs by domains. Plays for a playbook in order to achieve what you want:</p>
<p>Deploy certbot and nginx with no config files, then for each <strong>domain</strong> on host:</p>
<ol>
<li>Deploy an nginx config for default server with <code>location /.well-known/acme-challenge { root /etc/letsencrypt/challenges; }</code></li>
<li>Reload nginx config</li>
<li>Check existence of a certificate for the domain in <code>/etc/letsencrypt/live/domain</code></li>
<li>If cert is already presented, end the run</li>
<li><code>certbot certonly -n --web-root=/etc/letsencrypt/challenges --keep -d domain</code> to make a cert request</li>
<li>Check for cert in a retry loop</li>
<li>Fix permissions for live certificates</li>
<li>Deploy normal nginx configs</li>
</ol>
<p>You may deploy a stub config per domain instead of using the default server. The playbook could be run asynchronously for each domain.</p>

CC BY-SA 4.0
2020-08-10T22:31:32.740Z
Well, I already have only _one_ domain. It's not the domains that creates trouble but config with https and config for webroot without https. And running nginx with 2 different configs in one go. And I'm not sure if I can remove webroot from final config after acquiring cert. Your algorithm also has two phases for Nginx in one playbook. So it's basically mostly the same. Maybe I'm just overthinking it. Maybe it was already fine after all. It's just that Ansible's idea of describing state does not quite fit with such algorithms.
Gherman
CC BY-SA 4.0
2020-08-11T00:56:44.263Z
Nginx is made to run multiple configs by design, the stub one is needed for init phase of certbot anyway (it is injected in apache/nginx methods).
Location for ACME challenge should be in the actual config for a renewal (check the documentation).
About states: Check - Init - Ready is a default chain in so many deployment processes.
chupasaurus
CC BY-SA 4.0
2020-08-11T12:08:20.133Z
Can you point me to some source where Check - Init - Ready chain is mentioned? A book, article or docs of something.
Gherman
CC BY-SA 4.0
2020-08-11T18:49:23.530Z
It's rather a common sense: first you have to check the state, then change it to fulfill the requirements and actually deploy software and prepare it for action in the end. It's used everywhere, from package managers to K8s.
chupasaurus
CC BY-SA 4.0
2020-08-10T10:22:11.200Z
2020-08-10T10:22:11.200Z
chupasaurus
<p>I have a public web-site with Nginx in front of it. We use free Letsencrypt certificates to set up <code>https</code>. These are acquired and renewed by Certbot program. I want to implement entire servers configuration with Ansible playbooks. These playbooks have to:</p>
<ol>
<li>Automatically setup everything from scratch on a set of freshly dispatched empty CentOS instances.</li>
<li>Be able to setup with a different domain name if necessary</li>
<li>Be completely idempotent so I can make small changes and just run them as is.</li>
</ol>
<p>There are two similar problems I faced trying to achieve it.</p>
<p>The first is I need Nginx already running to fulfill challenges to acquire certificate and then run Nginx with different config to make use of now acquired certificate. When I acquire certificate Nginx has to serve some files for Letsencrypt to check. This incurs several problems:</p>
<ol>
<li>Playbook has to run Nginx twice in one go with different configs. This is tricky to express using Ansible because of descriptive nature of playbooks. Nginx now has different states in just one Playbook execution. I no longer express state but rather instructions.</li>
<li>Using several slightly different configs means either a lot of Nginx config code repetitions or complex and confusing <code>if</code>s in them.</li>
</ol>
<p>The second problem is that I wanted to put all my Nginx configuration under VCS so I want playbooks to come with already written Nginx configs. However this not the way recommended by Certbot. Certbot wants you to use a command that <em>parses</em> and then <em>generates</em> Nginx configs to add a certificate there. This may seem convenient but I this way I cannot be sure the results are going to be completely reproducible. Suppose I acquire cert and generate config, then change something by hand and then I lose my servers and domain name and try to run them from scratch. Then some new problems may occur which otherwise wouldn't have. My Nginx configs are complex with many services on different ports, conditions and other details. I have to control how it looks like.</p>
<p>I considered making several different playbooks for different cases. But this also leads to code repetitions and makes using them more complex for a newcoming employee. And rarely used playbooks or scripts may become out of sync.</p>
<p>I did achieve a solution that passes all these criteria. But I am not happy with it because it turned out rather complex with Nginx configuration happening twice and many <code>if</code>s and booleans variables. And it's not how Ansible playbooks are intended to be written. Is there a good way to do all this?</p>

<ansible><nginx><certbot><letsencrypt>
Snake bites its tail problem automating configuration of Certbot with Ansible
Gherman
# Snake bites its tail problem automating configuration of Certbot with Ansible

(Asked by: username_0 on 2020-08-05)

I have a public web-site with Nginx in front of it. We use free Letsencrypt certificates to set up `https`. These are acquired and renewed by Certbot program. I want to implement entire servers configuration with Ansible playbooks. These playbooks have to:

1. Automatically setup everything from scratch on a set of freshly dispatched empty CentOS instances.
2. Be able to setup with a different domain name if necessary
3. Be completely idempotent so I can make small changes and just run them as is.

There are two similar problems I faced trying to achieve it.

The first is I need Nginx already running to fulfill challenges to acquire certificate and then run Nginx with different config to make use of now acquired certificate. When I acquire certificate Nginx has to serve some files for Letsencrypt to check. This incurs several problems:

1. Playbook has to run Nginx twice in one go with different configs. This is tricky to express using Ansible because of descriptive nature of playbooks. Nginx now has different states in just one Playbook execution. I no longer express state but rather instructions.
2. Using several slightly different configs means either a lot of Nginx config code repetitions or complex and confusing `if`s in them.

The second problem is that I wanted to put all my Nginx configuration under VCS so I want playbooks to come with already written Nginx configs. However this not the way recommended by Certbot. Certbot wants you to use a command that *parses* and then *generates* Nginx configs to add a certificate there. This may seem convenient but I this way I cannot be sure the results are going to be completely reproducible. Suppose I acquire cert and generate config, then change something by hand and then I lose my servers and domain name and try to run them from scratch. Then some new problems may occur which otherwise wouldn't have. My Nginx configs are complex with many services on different ports, conditions and other details. I have to control how it looks like.

I considered making several different playbooks for different cases. But this also leads to code repetitions and makes using them more complex for a newcoming employee. And rarely used playbooks or scripts may become out of sync.

I did achieve a solution that passes all these criteria. But I am not happy with it because it turned out rather complex with Nginx configuration happening twice and many `if`s and booleans variables. And it's not how Ansible playbooks are intended to be written. Is there a good way to do all this?

**Tags:** ansible, nginx, certbot, letsencrypt


---

## 1 answers

### Answer by username_1 on 2020-08-10. Score: 1 | Accepted answer

Don't read the text written on Certbot site, the real talk is [in the documentation](https://certbot.eff.org/docs/using.html?highlight=hooks#webroot).

In order to make less actions, consider grouping configs by domains. Plays for a playbook in order to achieve what you want:

Deploy certbot and nginx with no config files, then for each **domain** on host:

1. Deploy an nginx config for default server with `location /.well-known/acme-challenge { root /etc/letsencrypt/challenges; }`
2. Reload nginx config
3. Check existence of a certificate for the domain in `/etc/letsencrypt/live/domain`
4. If cert is already presented, end the run
5. `certbot certonly -n --web-root=/etc/letsencrypt/challenges --keep -d domain` to make a cert request
6. Check for cert in a retry loop
7. Fix permissions for live certificates
8. Deploy normal nginx configs

You may deploy a stub config per domain instead of using the default server. The playbook could be run asynchronously for each domain.

> **Comments:**
> **username_0**: Well, I already have only _one_ domain. It's not the domains that creates trouble but config with https and config for webroot without https. And running nginx with 2 different configs in one go. And I'm not sure if I can remove webroot from final config after acquiring cert. Your algorithm also has two phases for Nginx in one playbook. So it's basically mostly the same. Maybe I'm just overthinking it. Maybe it was already fine after all. It's just that Ansible's idea of describing state does not quite fit with such algorithms.  
> **username_1**: Nginx is made to run multiple configs by design, the stub one is needed for init phase of certbot anyway (it is injected in apache/nginx methods). Location for ACME challenge should be in the actual config for a renewal (check the documentation). About states: Check - Init - Ready is a default chain in so many deployment processes.  
> **username_0**: Can you point me to some source where Check - Init - Ready chain is mentioned? A book, article or docs of something.  
> **username_1**: It's rather a common sense: first you have to check the state, then change it to fulfill the requirements and actually deploy software and prepare it for action in the end. It's used everywhere, from package managers to K8s.  

---

