<!-- livebook:{"app_settings":{"access_type":"public","output_type":"rich","slug":"hostkit-caddy-demo"}} -->
# Deploy a Caddy static site with HostKit
```elixir
host_kit_dep =
if File.exists?(Path.join(File.cwd!(), "mix.exs")) do
{:host_kit, path: File.cwd!()}
else
{:host_kit, github: "elixir-vibe/host_kit"}
end
Mix.install([
{:kino, "~> 0.19"},
{:req, "~> 0.5"},
host_kit_dep
])
```
## Setup
```elixir
Code.require_file("notebooks/learn/hostkit_demo_helpers.exs", File.cwd!())
Logger.configure(level: :notice)
alias HostKit, as: HK
alias HostKit.LivebookDemo, as: Demo
alias HostKit.Providers.Caddy
```
## Target
Use the local demo VM defaults, or replace them with your own server.
<details>
<summary>SSH key upload</summary>
Uploaded keys are copied to a temporary `0600` file in the Livebook runtime and used as the SSH identity file.
</details>
```elixir
form =
Demo.target_form(
server: "127.0.0.1",
user: "root",
password: "hostkit-demo",
ssh_port: 2222,
public_port: 18_080,
message: "Deployed by HostKit"
)
```
```elixir
settings = Demo.await_target(form)
target = %{
host: settings.server,
user: settings.user,
sudo: true,
ssh: Demo.ssh_opts(settings)
}
site_address = ":#{settings.public_port}"
message = settings.message
```
## Declare
A tiny static site, described as HostKit resources.
```elixir
deployment_name = "hostkit-caddy-demo"
acme_email = "admin@example.com"
site_root = "/srv/#{deployment_name}"
caddy_config_path = "/etc/#{deployment_name}/Caddyfile"
caddy_config_dir = Path.dirname(caddy_config_path)
caddy_sites_dir = "/etc/#{deployment_name}/sites"
caddy_service_name = "#{deployment_name}.service"
verify_url = "http://127.0.0.1#{site_address}"
public_url = "http://#{target.host}#{site_address}"
```
```elixir
html = """
<!doctype html>
<html>
<head><meta charset=\"utf-8\"><title>Hello from HostKit</title></head>
<body>
<h1>Hello from HostKit</h1>
<p>#{message}</p>
</body>
</html>
"""
caddyfile = """
{
admin off
email #{acme_email}
}
import #{caddy_sites_dir}/*.caddy
"""
use HK.DSL, providers: [Caddy]
project =
project :deploy_caddy_site do
host :target, at: target.host do
ssh Keyword.merge(target.ssh, user: target.user, sudo: target.sudo)
end
provider :caddy, Caddy do
set :sites_dir, caddy_sites_dir
end
service :hello_site do
package :caddy, as: "caddy"
package :curl, as: "curl"
directory site_root, owner: "root", group: "root", mode: 0o755
directory caddy_config_dir, owner: "root", group: "root", mode: 0o755
directory caddy_sites_dir, owner: "root", group: "root", mode: 0o755
file Path.join(site_root, "index.html"),
content: html,
owner: "root",
group: "root",
mode: 0o644
file caddy_config_path,
content: caddyfile,
owner: "root",
group: "root",
mode: 0o644
daemon unit: caddy_service_name do
description "HostKit demo Caddy site"
exec ["/usr/bin/caddy", "run", "--config", caddy_config_path]
restart :on_failure
end
caddy_site site_address do
root site_root
file_server()
end
ready :hello_site do
systemd caddy_service_name, restart: true
http verify_url
end
end
end
project
```
## Plan
Preview the changes. Nothing has been applied yet.
```elixir
host = hd(project.hosts)
target_opts = HostKit.Host.target_opts(host)
{:ok, plan} = HK.plan(project, target_opts)
```
```elixir
Demo.plan_summary(plan)
```
```elixir
Demo.plan_table(plan)
```
## Deploy
This is the line that changes the target.
```elixir
reporter = self()
apply_result = HK.apply(plan, Keyword.merge(target_opts, confirm: true, reporter: reporter))
Kino.nothing()
```
```elixir
progress = Demo.collect_apply_progress()
Demo.apply_summary(apply_result, progress)
```
```elixir
Demo.apply_table(apply_result)
```
## Verify
Readiness checks in the declaration already restart and wait for the service during apply. Verification can stay simple: fetch the public URL.
```elixir
response = Req.get!(public_url)
[
Demo.verify_summary(response, public_url),
Kino.HTML.new(response.body)
]
```