Trying to use the API

1. Caddy version (caddy version):

v2.4.6

2. How I run Caddy:

Ubuntu Service

a. System environment:

Ubuntu 20

b. Command:

N/a

c. Service/unit/compose file:

d. My complete Caddyfile or JSON config:

{
    email me@example.com
    acme_dns cloudflare API_KEY
}
aspecthq.com {
    redir https://www.aspecthq.com{uri}
}
www.aspecthq.com {
    root * /var/www/aspecthq.com
    log {
        output file /var/log/caddy/aspecthq.com/access.log {
            roll_size 3MiB
            roll_keep 5
            roll_keep_for 48h
        }
        format console
    }
    encode gzip zstd
    php_fastcgi unix//run/php/php8.1-fpm.sock
    file_server
    push
}

3. The problem I’m having:

I am trying to learn the API. When I read the docs, it makes sense. However, when I look at my CaddyFile, I get confused on how I would add this data via the API.

I hope to add more domains with pretty much the exact setup minus the domain and directories (obviously).

I am open to feedback on how I could optimize settings. It seems that many of the settings could be made global. I really only need to customize the www and log folders. I am fine with everything else being the same.

4. Error messages and/or full log output:

N/a

5. What I already tried:

I queried the config on my server with this CaddyFile in order to analyze the structure. The structure seems to be all over the place and not as easily confined like the Caddyfile.

6. Links to relevant resources:

n/a

Our Getting Started guide has the info you want. We recommend all users go through the Getting Started tutorial:

The Caddyfile is a config adapter, since Caddy’s native config format is JSON. You can use the API with your Caddyfile, but only by POSTing the whole Caddyfile to make changes. Using JSON lets you change specific parts of the config.

Hope that helps!

Like I said in my post, I read the Getting Started. From my perspective, its an extremely simple, non-real world example.

The Caddyfile that I have above is just your run go the mill, everyday website. That (to me) feels like a real world example to show. When I use that Caddyfile, this is the JSON config that Caddy returns.

{
  "apps": {
    "http": {
      "servers": {
        "srv0": {
          "listen": [
            ":443"
          ],
          "logs": {
            "logger_names": {
              "www.aspecthq.com": "log0"
            },
            "skip_hosts": [
              "aspecthq.com"
            ]
          },
          "routes": [
            {
              "handle": [
                {
                  "handler": "subroute",
                  "routes": [
                    {
                      "handle": [
                        {
                          "handler": "vars",
                          "root": "/var/www/aspecthq.com"
                        },
                        {
                          "encodings": {
                            "gzip": {},
                            "zstd": {}
                          },
                          "handler": "encode",
                          "prefer": [
                            "gzip",
                            "zstd"
                          ]
                        },
                        {
                          "handler": "push"
                        }
                      ]
                    },
                    {
                      "handle": [
                        {
                          "handler": "static_response",
                          "headers": {
                            "Location": [
                              "{http.request.uri.path}/"
                            ]
                          },
                          "status_code": 308
                        }
                      ],
                      "match": [
                        {
                          "file": {
                            "try_files": [
                              "{http.request.uri.path}/index.php"
                            ]
                          },
                          "not": [
                            {
                              "path": [
                                "*/"
                              ]
                            }
                          ]
                        }
                      ]
                    },
                    {
                      "handle": [
                        {
                          "handler": "rewrite",
                          "uri": "{http.matchers.file.relative}"
                        }
                      ],
                      "match": [
                        {
                          "file": {
                            "split_path": [
                              ".php"
                            ],
                            "try_files": [
                              "{http.request.uri.path}",
                              "{http.request.uri.path}/index.php",
                              "index.php"
                            ]
                          }
                        }
                      ]
                    },
                    {
                      "handle": [
                        {
                          "handler": "reverse_proxy",
                          "transport": {
                            "protocol": "fastcgi",
                            "split_path": [
                              ".php"
                            ]
                          },
                          "upstreams": [
                            {
                              "dial": "unix//run/php/php8.1-fpm.sock"
                            }
                          ]
                        }
                      ],
                      "match": [
                        {
                          "path": [
                            "*.php"
                          ]
                        }
                      ]
                    },
                    {
                      "handle": [
                        {
                          "handler": "file_server",
                          "hide": [
                            "./Caddyfile"
                          ]
                        }
                      ]
                    }
                  ]
                }
              ],
              "match": [
                {
                  "host": [
                    "www.aspecthq.com"
                  ]
                }
              ],
              "terminal": true
            },
            {
              "handle": [
                {
                  "handler": "subroute",
                  "routes": [
                    {
                      "handle": [
                        {
                          "handler": "static_response",
                          "headers": {
                            "Location": [
                              "https://www.aspecthq.com{http.request.uri}"
                            ]
                          },
                          "status_code": 302
                        }
                      ]
                    }
                  ]
                }
              ],
              "match": [
                {
                  "host": [
                    "aspecthq.com"
                  ]
                }
              ],
              "terminal": true
            }
          ]
        }
      }
    },
    "tls": {
      "automation": {
        "policies": [
          {
            "issuers": [
              {
                "challenges": {
                  "dns": {
                    "provider": {
                      "api_token": "APIKEY",
                      "name": "cloudflare"
                    }
                  }
                },
                "email": "joe@workmanmail.com",
                "module": "acme"
              },
              {
                "challenges": {
                  "dns": {
                    "provider": {
                      "api_token": "APIKEY",
                      "name": "cloudflare"
                    }
                  }
                },
                "email": "joe@example.com",
                "module": "zerossl"
              }
            ],
            "subjects": [
              "www.aspecthq.com",
              "aspecthq.com"
            ]
          }
        ]
      }
    }
  },
  "logging": {
    "logs": {
      "default": {
        "exclude": [
          "http.log.access.log0"
        ]
      },
      "log0": {
        "encoder": {
          "format": "console"
        },
        "include": [
          "http.log.access.log0"
        ],
        "writer": {
          "filename": "/var/log/caddy/aspecthq.com/access.log",
          "output": "file",
          "roll_keep": 5,
          "roll_keep_days": 2,
          "roll_size_mb": 3
        }
      }
    }
  }
}

Automating the building of that JSON definitely seems like a nightmare compared to the CaddyFile. I guess I will just stick with the Caddyfile way.

Sorry, I saw you said you read some docs, but didn’t specify which ones. So I wasn’t sure.

Why would you automate the building of that JSON though? I don’t understand why that is necessary if all you want to do is add a domain name.

You can either just add the domain to your Caddyfile and use POST /load, or you can traverse into the JSON to add it directly with the /config/... endpoints. If you’re maintaining a Caddyfile, though, updating your JSON config will make your Caddyfile outdated, like the docs explain.

I guess I’m wondering why the JSON is of concern to you when you are using a Caddyfile for a simple server and you seem to be making changes by hand.

Note that the Caddyfile doesn’t produce the most minimal, elegant JSON. It just produces correct JSON. Many JSON configs end up being simpler when written manually (or automated directly from other scripts/programs) because humans are smarter than the Caddyfile adapter.

1 Like

My thoughts were that if I wanted to add a new website to my server, the API would be a simple automated way to do that. Maybe even build a simple PHP page to make it all nice with a form. But It seems that the Caddyfile is easiest way to do that.

Sorry to keep bugging you. I am just trying to see how far I am push Caddy automation.

No worries.

The API is a simple way to automate config changes. And I think your sites could have a very nice, elegant JSON config. But not from the Caddyfile adapter – that will almost never be the case. But Caddy JSON is very expressive and powerful, so you can write a more consolidated, elegant config than whatever uncreative output the Caddyfile adapter gives you. For example, you could almost certainly eliminate the subroutes the Caddyfile creates. And if all your sites share the same logic, you can express that logic just once in the JSON, then use matchers and variables to apply that one route to all your domains equivalently. But the Caddyfile doesn’t think that way.

I have an article on the basics of mastering Caddy JSON, for sponsors: https://matt.life/expert-caddy/mastering-json (see Chapters - Expert Caddy for the index) - but it’s far from complete, I have plans to write more as sponsorships make it possible.

1 Like