January 27, 2022

How to Setup Caddy to Fetch its Config on Startup

configure caddy remotely

Why fetch Caddy's config?

This guide is a follow-up on how to remotely configure Caddy. If you're provisioning instances of Caddy at scale, you need centralize their configurations in a web service. As discussed in the previous guide, we can call Caddy's admin API to change its configuration. However, what should Caddy be doing on startup? If my configuration lives in another service, how do I get Caddy to use that configuration right off the bat? Turns out, the solution is baked into Caddy!

A Generic, Default Config File

When spinning up an instance of Caddy, you'll need to give it some default configuration file so that it's useful. You can actually use a config whose only instruction is to tell Caddy to fetch another config via HTTP call like so:

{
 "admin": {
   "config": {
     "load": {
       "module": "http",
       "url": "https://example.com/config/{env.INSTANCE_ID}",
     }
   }
 }
}

Now, the url is just an example: you need to have your web service host an endpoint that can return a Caddyfile or a JSON config for Caddy to load.

Notice the "{env.INSTANCE_ID}" at the end of the url. This is a handy feature that Caddy enables to allow us to read an environment variable! You might need something that indicates which Caddy instance is requesting its config. For example, each of your customers might have their own instance of Caddy with proprietary settings found in your database. Your web service could do a lookup of this data when this request comes through.

Note: Using environment variables in the HTTP loader module doesn't work in Caddy v2.4.6, but will be in the next version of Caddy.

The Response

What should your web service send back to Caddy? The docs tell us that you can send back any format that Caddy accepts as a configuration file. To keep things scalable, you'll likely want to stick to returning JSON.

Specify the configuration type using the "Content-Type" header in your response. For a JSON config, the header value would be "application/json" if you want to use Caddyfiles, you can send back a plain text response with a header value of "text/caddyfile".

Security!

Obviously, its not safe to just host an endpoint that returns Caddy configurations willy-nilly. You need some security layer here. You could do IP filtering, or maybe you're communicating inside a VPC. If not, you could add some header that includes an API key like so:

{
 "admin": {
   "config": {
     "load": {
       "module": "http",
       "url": "https://example.com/config/{env.INSTANCE_ID}",
       "header": {
         "x-api-key": [
           "{env.API_KEY}"
         ]
       }
     }
   }
 }
}

Just be sure to have your environment loaded with the variable API_KEY. Or you could use another one of Caddy's placeholders.

Conclusion

Congratulations! If you've made use of the first half of this guide as well, you've successfully decentralized your management of Caddy. The possibilities are endless!

If I missed anything, reach out to me on Twitter and I'd be happy to make an edit.

If your company would like Caddy managed for you, contact us below.

Ready to scale?

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

or schedule a call:

Let's Meet