Using Caddy API in SaaS solutions

1. Caddy version (caddy version):

v2.3.0 h1:fnrqJLa3G5vfxcxmOH/+kJOcunPLhSBnjgIvjXV/QTA=

2. How I run Caddy:

I want my customers to register an account and select a plan. Then, we will create a new subdomain for them and reload the Caddy’s config.

The current setup has two servers:

  1. One server for registration which has Nginx
  2. Another one is for serving the subdomains created by the customers and it uses Caddy.

Now there are two things that I like about Caddy and made us try it:

  1. Automatic HTTPS
  2. Ability to load new configuration using the API without any CLI commands.

The problem we have currently is the deadlock issue after calling [POST ]/load API of the Caddy admin:

"msg": "stopping current admin endpoint"
"error": "shutting down admin server: context deadline exceeded"

a. System environment:

Ubuntu 20.04.2 LTS

b. Command:

We used the following commands after installing Caddy:

systemctl disable caddy.service
systemctl enable caddy-api.service
systemctl start caddy-api.service

c. My complete Caddyfile or JSON config:

tenant-subdomain.ahmedhat.com {
    try_files {path} {path}/ /index.html?{query}
    root * /var/www/ahmedhat-spa/build
    file_server
}

This block will be repeated for each customer registered in our solution but tenant-subdomain will be replaced by the their choice of subdomain.

3. The problem I’m having:

When a customer subscribes, the first server will create an account and will call the [POST] /load API to apply the new config but issue we are getting into is the deadlock.

4. Error messages and/or full log output:

{"level":"error","ts":1587571718.9594,"logger":"admin","msg":"stopping current admin endpoint","error":"shutting down admin server: context deadline exceeded"}

5. What I already tried:

  1. I exposed the localhost:2019 on the 2060 port by using Caddy but I went to the deadlock after calling http://193.122.75.178:2060/load API. 193.122.75.178 is the IP address of the server with Caddy.
  2. I created a small project that will receive the Caddyfile content and call localhost:2019/load API. This project will be set up on the second server (the one that has Caddy). This project will be exposed to the first server by using Nginx on a custom port.

The other approach is working fine currently and I can limit the access to Nginx for certain IPs, or actually the IP of the first server only. But my issue with the second approach is that I am using Nginx which I feel it is not the right approach.

So my question what is the right approach in my current setup?

I think On-Demand TLS would probably be a better choice for you here, rather than dynamically adding more sites to your config.

{
	on_demand_tls {
		ask https://example.com/ask
	}
}

:443 {
	tls {
		on_demand
	}

	root * /var/www/ahmedhat-spa/build
	try_files {path} {path}/ /index.html?{query}
	file_server
}

The likely reason you’re encountering a deadlock is that if Caddy is serving the request that triggered the reload, the admin app is waiting for all the requests from the old config to finish, but that can never happen because the request that triggered the reload is waiting for the admin config to change. Chicken and egg problem. Deeper explanation here:

If you’re only serving (an unbounded number of) subdomains, you should probably use a single wildcard certificate instead. Way less error prone.

2 Likes

Thanks Francis.

It is my bad. I missed this feature while looking into the docs unfortunately :sweat_smile:.

But what is the best approach to utilize the Caddy’s API in such a scenario? When I first heard of Caddy and it has an API to configure, the first thing that came into my mind is that I can use it in my SaaS solutions but from what I see is that it has to be configured in such a way to prevent the deadlock issue which seems for me to defeat the purpose of the API.

Thanks Matt and Francis. I really appreciate your valuable time.

I am working in a venture builder and we have many projects in the pipeline and some of them are SaaS solutions.

@matt Your solution is totally fine but our plan is to introduce the domain feature later, not only the subdomain. @francislavoie has suggested looking into On-demand TLS which is also a valid solution. However, I really like Caddy and its simplicity so the following question is to understand Caddy’s better and your vision about the API:

What is the best approach to utilize the Caddy’s API in such a scenario? When I first heard of Caddy and it has an API to update the configuration, the first thing that came into my mind is that I can use it in my SaaS solutions but from what I see is that it has to be configured in such a way to prevent the deadlock issue which seems for me to defeat the purpose of the API.

And thank you for this great product.

It would only deadlock if you’re proxying to the admin endpoint with a server that the admin endpoint is itself controlling/configuring (this is explained in the issue Francis linked to).

So, don’t do that? Just access it directly, don’t proxy to it through a server that is configured by the admin API.

You should perform the actual admin API changes in a job queue or something, in another process, i.e. not directly from a request coming through Caddy.

This topic was automatically closed after 30 days. New replies are no longer allowed.