Reduce lines in JSON adaptation of CaddyFile

1. Transitioning to API usage, difficulty with paths and JSON maintenance

TLDR:

  • How can the complexity of my CaddyFile be reduced to result in a less complex JSON?
  • Why can’t @ID’s be added to CaddyFile to be inserted into JSON upon adaptation?
  • Why is it so difficult to determine the path in a JSON for API usage?
  • How is a long JSON maintained? It’s not obviously easy to make manual edits like adding path handlers and reverse proxies. Can work like this be done with the API?

Adapted from what I thought was a simple 52 line CaddyFile, the resulting JSON is almost 700 lines.

I want to use the PATCH command to add basic auth entries under multiple apps in the config, and to me, reading the JSON and figuring out the path is not so simple.

I had success manually entering a @id into a part of the config, and using the patch command. I also tried ChatGPT to find the right path, but that was unsuccessful.

Basically, I see two options.

Every time I want to update the config or add an app, I edit the CaddyFile, adapt it to JSON, and then manually insert all the @ID tags.

OR

Maintain the JSON itself, which is very long to edit by hand.

Am I missing something? I feel like this should be simpler than Im making it.

I wish I could insert @ID’s into the CaddyFile and make pathing easier, but after some searching this does not seem possible.

3. Caddy version:

v2.8.4

4. How I installed and ran Caddy:

I installed caddy on linux and have run from CaddyFile and adapted JSON

a. System environment:

Ubuntu 24 on raspberry pi

d. My complete Caddy config:

Here is my Caddyfile:

(basic-auth) {
	route {
		header User-ID {http.auth.user.id}
	}
	basic_auth {
		email@email.com hash
	}
}

website.com {
	root * /home/lesko/public-website
	file_server
	header Access-Control-Expose-Headers "User-ID"

	handle_path /employees/pto/request* {
		import basic-auth
		reverse_proxy 0.0.0.0:8502
	}

	handle_path /employees/pto/total* {
		import basic-auth
		reverse_proxy 0.0.0.0:8503
	}

	handle_path /employees/pto/approve* {
		import basic-auth
		reverse_proxy 0.0.0.0:8504
	}

	handle_path /employees/pto/calendar* {
		import basic-auth
		reverse_proxy 0.0.0.0:8505
	}

	handle_path /employees/pto/manage* {
		import basic-auth
		reverse_proxy 0.0.0.0:8506
	}

	handle_path /employees/supplies* {
		reverse_proxy 0.0.0.0:8507
	}

	handle_path /employees/query_sql* {
		import basic-auth
		reverse_proxy 0.0.0.0:8508
	}

	handle_path /employees/database* {
		import basic-auth
		reverse_proxy 0.0.0.0:8509
	}

	handle_path /employees* {
		import basic-auth
		reverse_proxy 0.0.0.0:8001
	}
}

1 Like

A few of those questions (such as how you maintain your JSON) are exercises in preference of the administrator. I’m not sure about the API question - but I can say the API is absolutely usable to make small scoped changes in the configuration, yes. Maybe you’re asking if there’s some kind of interface to help you with that - there is no official user interface for the API, you’ll need to craft your own requests to interact with it. Normally this is done programmatically rather than by hand; it is an application programming interface after all.

The path for any config in the API is just /config/ + the slash-separated nested JSON path.

Check out the example at https://caddyserver.com/docs/api#get-configpath to see what I mean.

I suspect, in a word, “complexity”. The Caddyfile format isn’t a first-class configuration language to the Caddy program itself, it’s just a convenience for the user that Caddy translates to JSON internally. What I mean by that is that of course we love and maintain the Caddyfile adapter and it’s absolutely important to us as Caddy users, but Caddy itself doesn’t “think” in Caddyfile syntax, it’s all JSON under the hood. That means there’s abstractions.

In that respect it would probably often be unclear to the end user exactly how and where an ID will end up, nor does it seem straightforward to me how such an ID would be specified in Caddyfile syntax, nor does it seem like it’s particularly useful to Caddyfile users in general.

You have a large amount of configuration amplification happening via your snippet.

Snippets aren’t a thing in JSON. When you import one, Caddy literally replaces the import <snippet> text with the actual contents of the snippet before parsing it all into JSON. That means what’s neat in a Caddyfile turns into replicated configuration in JSON at every single path you’ve imported the snippet to.

First off, what’s that route doing? Nothing except adding extra lines of JSON config. The purpose of route is to manually order nested handler directives… But you have literally only a single handler directive. Technically you could use a route to scope nested directives to a matcher… But you aren’t doing that either. Removing route here will save you a whole bunch of JSON lines.

Secondly, you don’t even need a snippet at all.

You have as a final handle_path the following:

And every other handle is beneath /employees. That means every single request to /employees* is getting a basic auth and nowhere else is.

So why snippet this into each handle when you can just set it outside, once?

A simple basic_auth /employees* { email@email.com hash } would solve this for you. Then remove the snippets entirely.

Now all you’ll have left is each handle_path, which strips the URI prefix and proxies upstream. That much will need to stay.

2 Likes

To keep it plain, you need to go all-in on JSON, or all-in on Caddyfile. You can’t really have one foot in both. If you use the API to push changes to your live config on the server, there’s no way to pull that back into the Caddyfile. The Caddyfile to JSON adapter is one-way.

2 Likes