**If you [installed Caddy](/docs/install) from a package manager, Caddy might already be running as a service. If so, please stop the service before doing this tutorial.**
Caddy is controlled through the `caddy` command. It offers many useful tools for different tasks while running Caddy. Have a closer look at [ `caddy`'s subcommands ](https://caddyserver.com/docs/command-line) to learn what you can do.
This blocks forever, but what is it doing? At the moment... nothing. By default, Caddy's configuration ("config") is blank. We can verify this using the [admin API](/docs/api) in another terminal:
This is <b>not</b> your website: the administration endpoint at localhost:2019 is used for controlling Caddy and is restricted to localhost by default.
We can make Caddy useful by giving it a config. This can be done many ways, but we'll start by making a POST request to the [/load](/docs/api#post-load) endpoint using `curl` in the next section.
You do not have to use config files, but we are for this tutorial. Caddy's <ahref="/docs/api">admin API</a> is designed for use by other programs or scripts.
If you see _Hello, world!_, then congrats -- it's working! It's always a good idea to make sure your config works as you expect, especially before deploying into production.
If you made no errors, you will see JSON output! What happened here?
We just used a [_config adapter_](/docs/config-adapters) to convert our Caddyfile to Caddy's native JSON structure.
<asideclass="tip"markdown="1">
It is always good to test the changes in your `Caddyfile` using `caddy adapt`. If there are errors in your `Caddyfile`, you will see where you made a mistake instead of the JSON.
While we could take that output and make another API request, we can skip all those steps because the `caddy` command can do it for us. If there is a file called Caddyfile in the current directory and no other config is specified, Caddy will load the Caddyfile, adapt it for us, and run it right away.
The Caddyfile seems easier than JSON, but should you always use it? There are pros and cons to each approach. The answer depends on your requirements and use case.
The Caddyfile offers shortcuts for many common tasks that are require complex JSON configurations. Use `caddy adapt` to learn how these shortcuts work in JSON.
</aside>
It is important to note that both JSON and the Caddyfile (and [any other supported config adapter](/docs/config-adapters)) can be used with [Caddy's API](/docs/api). However, you only get the full range of Caddy's functionality and API features if you use JSON. If using a config adapter, the only way to load or change the config with the API is the [/load endpoint](/docs/api#post-load).
You will also want to decide whether your workflow is API-based or CLI-based. (You _can_ use both the API and config files on the same server, but we don't recommend it: best to have one source of truth.)
API | Config files
----|-------------
Make config changes with HTTP requests | Make config changes with shell commands
Easy to scale | Difficult to scale
Difficult to manage by hand | Easy to manage by hand
The choice of API or config file workflow is orthogonal to the use of config adapters: you can use JSON but store it in a file and use the command line interface; conversely, you can also use the Caddyfile with the API.
Since Caddy is a server, it runs indefinitely. That means your terminal won't unblock after you execute `caddy run` until the process is terminated (usually with Ctrl+C).
Although `caddy run` is the most common and is usually recommended (especially when making a system service!), you can alternatively use `caddy start` to start Caddy and have it run in the background:
When using the command line, however, it may be tempting to use Ctrl+C to stop your server and then restart it again to pick up the new configuration. Don't do this: stopping and starting the server is orthogonal to config changes, and will result in downtime.
This actually just uses the API under the hood. It will load and, if necessary, adapt your config file to JSON, then gracefully replace the active configuration without downtime.
Technically, the new config is started before the old config is stopped, so for a brief time, both configs are running! If the new config fails, it aborts with an error, while the old one is simply not stopped.