2026-06-16 09:39:36 +00:00
#!/usr/bin/env bash
set -euo pipefail
ROOT = " $( cd " $( dirname " ${ BASH_SOURCE [0] } " ) /.. " && pwd ) "
cd " $ROOT "
read_coolify_env( ) {
key = " $1 "
default_value = " $2 "
current_value = " ${ !key :- } "
if [ -n " $current_value " ] ; then
printf '%s\n' " $current_value "
return
fi
if [ -f .env ] ; then
env_value = " $( grep -E " ^ ${ key } = " .env 2>/dev/null | tail -n1 | cut -d= -f2- | sed " s/^['\"]//; s/['\"] $// " ) "
if [ -n " $env_value " ] ; then
printf '%s\n' " $env_value "
return
fi
fi
printf '%s\n' " $default_value "
}
coold_vm_count( ) {
local count
count = " $( read_coolify_env COOLIFY_COOLD_VM_COUNT 2) "
if [ " $count " != "1" ] && [ " $count " != "2" ] ; then
echo "ERROR: COOLIFY_COOLD_VM_COUNT supports 1 or 2 for now." >& 2
exit 1
fi
printf '%s\n' " $count "
}
coold_vm_instance( ) {
local index = " $1 "
local base
base = " $( read_coolify_env COOLIFY_COOLD_LIMA_INSTANCE coold-dev) "
if [ " $index " = "1" ] ; then
printf '%s\n' " $base "
return
fi
read_coolify_env " COOLIFY_COOLD_LIMA_INSTANCE_ ${ index } " " ${ base } - ${ index } "
}
coold_vm_wg_ip( ) {
local index = " $1 "
2026-06-16 10:33:26 +00:00
read_coolify_env " COOLIFY_COOLD_VM_WG_IP_ ${ index } " " 100.64.0. ${ index } "
2026-06-16 09:39:36 +00:00
}
coold_vm_wg_port( ) {
local index = " $1 "
read_coolify_env " COOLIFY_COOLD_VM_WG_PORT_ ${ index } " " $(( 51820 + index)) "
}
coold_vm_container_subnet( ) {
local index = " $1 "
read_coolify_env " COOLIFY_COOLD_VM_CONTAINER_SUBNET_ ${ index } " " 10.210. $(( index - 1 )) .0/24 "
}
coold_vm_container_gateway( ) {
local index = " $1 "
read_coolify_env " COOLIFY_COOLD_VM_CONTAINER_GATEWAY_ ${ index } " " 10.210. $(( index - 1 )) .1 "
}
2026-06-16 10:33:26 +00:00
host_arch( ) {
case " $( uname -m) " in
x86_64| amd64)
printf '%s\n' amd64
; ;
arm64| aarch64)
printf '%s\n' arm64
; ;
*)
echo " ERROR: unsupported host architecture: $( uname -m) " >& 2
exit 1
; ;
esac
}
host_os( ) {
case " $( uname -s) " in
Darwin)
printf '%s\n' darwin
; ;
Linux)
printf '%s\n' linux
; ;
*)
echo " ERROR: unsupported host OS: $( uname -s) " >& 2
exit 1
; ;
esac
}
2026-06-16 11:57:42 +00:00
coolify_cli_bin( ) {
printf '%s\n' " $ROOT /.dev/bin/coolify "
2026-06-16 10:33:26 +00:00
}
2026-06-16 11:57:42 +00:00
ensure_coolify( ) {
2026-06-16 10:33:26 +00:00
local bin
local version
local arch
local os
local url
local tmpdir
2026-06-16 11:57:42 +00:00
bin = " $( coolify_cli_bin) "
version = " $( read_coolify_env COOLIFY_CLI_VERSION " $( read_coolify_env COOLIFY_COOLD_VERSION nightly) " ) "
2026-06-16 10:33:26 +00:00
arch = " $( host_arch) "
os = " $( host_os) "
2026-06-16 11:57:42 +00:00
if [ -x " $bin " ] && " $bin " --version >/dev/null 2>& 1 && [ " ${ COOLIFY_CLI_FORCE_DOWNLOAD :- false } " != "true" ] ; then
2026-06-16 10:33:26 +00:00
return
fi
2026-06-16 11:57:42 +00:00
url = " https://github.com/coollabsio/coold/releases/download/ ${ version } /coolify- ${ os } - ${ arch } .tar.gz "
echo " ==> Installing coolify from ${ url } "
2026-06-16 10:33:26 +00:00
mkdir -p " $( dirname " $bin " ) "
tmpdir = " $( mktemp -d) "
2026-06-16 11:57:42 +00:00
if ! curl -fsSL --retry 3 --max-time 120 -o " $tmpdir /coolify.tar.gz " " $url " ; then
rm -rf " $tmpdir "
return 1
fi
tar -xzf " $tmpdir /coolify.tar.gz " -C " $tmpdir "
install -m 0755 " $tmpdir /coolify " " $bin "
rm -rf " $tmpdir "
2026-06-16 10:33:26 +00:00
}
lima_ssh_target( ) {
local index = " $1 "
local instance
instance = " $( coold_vm_instance " $index " ) "
2026-06-16 11:57:42 +00:00
if [ ! -f " $HOME /.lima/ ${ instance } /ssh.config " ] ; then
echo " ERROR: Lima SSH config for ${ instance } was not found. Start it first with scripts/dev.sh up. " >& 2
2026-06-16 10:33:26 +00:00
exit 1
fi
2026-06-16 11:57:42 +00:00
printf 'lima-%s\n' " $instance "
2026-06-16 10:33:26 +00:00
}
2026-06-16 11:57:42 +00:00
lima_ssh_config( ) {
local count
local config = " $ROOT /.dev/lima/ssh.config "
local instance
count = " $( coold_vm_count) "
mkdir -p " $( dirname " $config " ) "
: > " $config "
for index in $( seq 1 " $count " ) ; do
instance = " $( coold_vm_instance " $index " ) "
if [ ! -f " $HOME /.lima/ ${ instance } /ssh.config " ] ; then
echo " ERROR: Lima SSH config for ${ instance } was not found. Start it first with scripts/dev.sh up. " >& 2
exit 1
fi
cat " $HOME /.lima/ ${ instance } /ssh.config " >> " $config "
printf '\n' >> " $config "
done
printf '%s\n' " $config "
}
coolify_nodes_arg( ) {
2026-06-16 10:33:26 +00:00
local count
local nodes = ""
2026-06-16 11:57:42 +00:00
local node
2026-06-16 10:33:26 +00:00
count = " $( coold_vm_count) "
for index in $( seq 1 " $count " ) ; do
2026-06-16 11:57:42 +00:00
node = " $( lima_ssh_target " $index " ) " || return 1
2026-06-16 10:33:26 +00:00
if [ -n " $nodes " ] ; then
nodes = " ${ nodes } , "
fi
2026-06-16 11:57:42 +00:00
nodes = " ${ nodes } ${ node } "
2026-06-16 10:33:26 +00:00
done
printf '%s\n' " $nodes "
}
2026-06-16 11:57:42 +00:00
coolify_wg_listen_overrides_arg( ) {
2026-06-16 10:33:26 +00:00
local count
local overrides = ""
local node
count = " $( coold_vm_count) "
for index in $( seq 1 " $count " ) ; do
node = " $( lima_ssh_target " $index " ) "
if [ -n " $overrides " ] ; then
overrides = " ${ overrides } , "
fi
overrides = " ${ overrides } ${ node } = $( coold_vm_wg_port " $index " ) "
done
printf '%s\n' " $overrides "
}
2026-06-16 11:57:42 +00:00
coolify_wg_endpoint_overrides_arg( ) {
2026-06-16 10:33:26 +00:00
local count
local overrides = ""
local node
count = " $( coold_vm_count) "
for index in $( seq 1 " $count " ) ; do
node = " $( lima_ssh_target " $index " ) "
if [ -n " $overrides " ] ; then
overrides = " ${ overrides } , "
fi
overrides = " ${ overrides } ${ node } =host.lima.internal: $( coold_vm_wg_port " $index " ) "
done
printf '%s\n' " $overrides "
}
2026-06-16 11:57:42 +00:00
coolify_ssh_key( ) {
read_coolify_env COOLIFY_CLI_SSH_KEY " $HOME /.lima/_config/user "
2026-06-16 10:33:26 +00:00
}
2026-06-16 11:57:42 +00:00
coolify_ssh_user( ) {
read_coolify_env COOLIFY_CLI_SSH_USER " $USER "
2026-06-16 10:33:26 +00:00
}
2026-06-16 11:57:42 +00:00
coolify_bootstrap_command( ) {
local nodes
local ssh_config
local listen_overrides
local endpoint_overrides
ensure_coolify
nodes = " $( coolify_nodes_arg) " || return 1
ssh_config = " $( lima_ssh_config) " || return 1
listen_overrides = " $( coolify_wg_listen_overrides_arg) " || return 1
endpoint_overrides = " $( coolify_wg_endpoint_overrides_arg) " || return 1
2026-06-16 10:33:26 +00:00
cat <<CMD
2026-06-16 11:57:42 +00:00
$( coolify_cli_bin) init bootstrap \\
--nodes " ${ nodes } " \\
--ssh-config " ${ ssh_config } " \\
--ssh-user " $( coolify_ssh_user) " \\
--wg-listen-port-overrides " ${ listen_overrides } " \\
--wg-endpoint-overrides " ${ endpoint_overrides } " \\
2026-06-16 10:33:26 +00:00
--coold-version " $( read_coolify_env COOLIFY_COOLD_VERSION nightly) " \\
--corrosion-version " $( read_coolify_env COOLIFY_CORROSION_VERSION v1.0.0) " \\
--yes
CMD
}
2026-06-16 11:57:42 +00:00
coolify_bootstrap( ) {
local nodes
local ssh_config
local listen_overrides
local endpoint_overrides
ensure_coolify
nodes = " $( coolify_nodes_arg) " || return 1
ssh_config = " $( lima_ssh_config) " || return 1
listen_overrides = " $( coolify_wg_listen_overrides_arg) " || return 1
endpoint_overrides = " $( coolify_wg_endpoint_overrides_arg) " || return 1
" $( coolify_cli_bin) " init bootstrap \
--nodes " $nodes " \
--ssh-config " $ssh_config " \
--ssh-user " $( coolify_ssh_user) " \
--wg-listen-port-overrides " $listen_overrides " \
--wg-endpoint-overrides " $endpoint_overrides " \
2026-06-16 10:33:26 +00:00
--coold-version " $( read_coolify_env COOLIFY_COOLD_VERSION nightly) " \
--corrosion-version " $( read_coolify_env COOLIFY_CORROSION_VERSION v1.0.0) " \
--yes
}
2026-06-16 13:45:16 +00:00
coolify_bootstrap_with_retry( ) {
local attempt
local attempts = 5
for attempt in $( seq 1 " $attempts " ) ; do
if coolify_bootstrap; then
return
fi
if [ " $attempt " = " $attempts " ] ; then
return 1
fi
echo " WARN: coolify bootstrap failed on attempt ${ attempt } / ${ attempts } ; retrying because fresh Lima hosts can finish setup after partial bootstrap phases... " >& 2
sleep 3
done
}
2026-06-16 11:57:42 +00:00
coolify_dev( ) {
2026-06-16 10:33:26 +00:00
local command = " ${ 1 :- help } "
if [ $# -gt 0 ] ; then
shift
fi
case " $command " in
install)
2026-06-16 11:57:42 +00:00
ensure_coolify
" $( coolify_cli_bin) " --version
2026-06-16 10:33:26 +00:00
; ;
path)
2026-06-16 11:57:42 +00:00
ensure_coolify
coolify_cli_bin
2026-06-16 10:33:26 +00:00
; ;
bootstrap-command)
2026-06-16 11:57:42 +00:00
coolify_bootstrap_command
2026-06-16 10:33:26 +00:00
; ;
run)
2026-06-16 11:57:42 +00:00
ensure_coolify
exec " $( coolify_cli_bin) " " $@ "
2026-06-16 10:33:26 +00:00
; ;
-h| --help| help )
cat <<'USAGE'
2026-06-16 11:57:42 +00:00
Usage: scripts/dev.sh coolify <command>
2026-06-16 10:33:26 +00:00
Commands:
2026-06-16 11:57:42 +00:00
install Download/install the nightly coolify dev binary
path Print the local coolify path
2026-06-16 10:33:26 +00:00
bootstrap-command Print the dev Lima bootstrap command without running it
2026-06-16 11:57:42 +00:00
run <args> Run coolify with arbitrary args
2026-06-16 10:33:26 +00:00
Example:
2026-06-16 11:57:42 +00:00
scripts/dev.sh coolify bootstrap-command
2026-06-16 10:33:26 +00:00
USAGE
; ;
*)
2026-06-16 11:57:42 +00:00
echo " unknown coolify command: $command " >& 2
echo "Run: scripts/dev.sh coolify help" >& 2
2026-06-16 10:33:26 +00:00
exit 1
; ;
esac
}
2026-06-16 09:39:36 +00:00
coold_vm( ) {
local index = " $1 "
shift
COOLIFY_COOLD_LIMA_INSTANCE = " $( coold_vm_instance " $index " ) " \
COOLIFY_COOLD_VM_WG_IP = " $( coold_vm_wg_ip " $index " ) " \
COOLIFY_COOLD_VM_CONTAINER_SUBNET = " $( coold_vm_container_subnet " $index " ) " \
COOLIFY_COOLD_VM_CONTAINER_GATEWAY = " $( coold_vm_container_gateway " $index " ) " \
scripts/coold-vm.sh " $@ "
}
mint_host_jwt_for_host( ) {
local host_id = " $1 "
local attempts = 60
local output
local caps
local builder_capacity
builder_capacity = " $( read_coolify_env COOLIFY_COOLD_VM_BUILDER_CAPACITY 2) "
caps = "coold"
if [ " $builder_capacity " != "0" ] ; then
caps = "coold,builder"
fi
for attempt in $( seq 1 " $attempts " ) ; do
if output = " $( spin exec -T coolify php artisan flux:dev " $host_id " --caps= " $caps " 2>& 1) " ; then
printf '%s\n' " $output " | tail -n 1
return 0
fi
echo " ==> Waiting for Flux dev JWT key for ${ host_id } ( ${ attempt } / ${ attempts } )... " >& 2
printf '%s\n' " $output " | tail -n 3 | sed 's/^/[coolify] /' >& 2
sleep 2
done
echo " ERROR: Could not mint Flux dev host JWT for ${ host_id } . " >& 2
return 1
}
follow_logs( ) {
local coold_vm_enabled = " $1 "
local count
local vm_logs_pids = ""
count = " $( coold_vm_count) "
echo "==> Following dev logs. Press Ctrl-C to stop the dev environment."
cleanup_logs( ) {
for pid in $vm_logs_pids ; do
kill " $pid " >/dev/null 2>& 1 || true
done
}
stop_from_signal( ) {
trap - INT TERM EXIT
cleanup_logs
echo
echo "==> Ctrl-C received; stopping dev environment..."
down
exit 130
}
trap cleanup_logs EXIT
trap stop_from_signal INT TERM
if [ " $coold_vm_enabled " != "false" ] ; then
for index in $( seq 1 " $count " ) ; do
instance = " $( coold_vm_instance " $index " ) "
coold_vm " $index " logs-agent | sed " s/^/[ ${ instance } ] / " &
vm_logs_pids = " $vm_logs_pids $! "
done
fi
spin logs -f
}
2026-06-16 13:45:16 +00:00
sync_v5_dev_lima_servers( ) {
2026-06-16 15:10:40 +00:00
local ssh_user
2026-06-16 13:45:16 +00:00
2026-06-16 15:10:40 +00:00
ssh_user = " $( coolify_ssh_user) "
2026-06-16 13:45:16 +00:00
echo "==> Running pending migrations before syncing v5 dev Lima state..."
spin exec -T coolify php artisan migrate --force
2026-06-16 15:10:40 +00:00
echo "==> Seeding dev Lima VM(s) into v5 clusters/servers..."
spin exec -T \
-e COOLIFY_CLI_SSH_USER = " $ssh_user " \
coolify php artisan db:seed --class= V5DevLimaSeeder --force
2026-06-16 13:45:16 +00:00
}
2026-06-16 10:33:26 +00:00
configure_flux_dev_for_vm( ) {
local index = " $1 "
local host_id
local host_jwt
local flux_url
host_id = " $( coold_vm_wg_ip " $index " ) "
flux_url = " $( read_coolify_env COOLIFY_COOLD_VM_FLUX_URL http://host.lima.internal:6443) "
echo " ==> Minting Flux dev host JWT for ${ host_id } ... "
host_jwt = " $( mint_host_jwt_for_host " $host_id " ) "
echo " ==> Installing Flux dev env/JWT into $( coold_vm_instance " $index " ) ... "
coold_vm " $index " install-host-jwt " $host_jwt "
COOLIFY_COOLD_LIMA_INSTANCE = " $( coold_vm_instance " $index " ) " scripts/coold-vm.sh shell <<SH
set -e
sudo install -d -m 0755 /etc/systemd/system/coold.service.d
sudo tee /etc/systemd/system/coold.service.d/10-flux-dev.conf >/dev/null <<UNIT
[ Service]
Environment = COOLIFY_COOLD_FLUX_URL = ${ flux_url }
Environment = COOLIFY_COOLD_HOST_JWT_PATH = /etc/coolify/host-jwt
UNIT
sudo systemctl daemon-reload
sudo systemctl restart coold.service
SH
}
2026-06-16 09:39:36 +00:00
up( ) {
local coold_vm_enabled
local follow_dev_logs
local count
2026-06-16 13:45:16 +00:00
local naked = false
local spin_args = ( )
2026-06-16 09:39:36 +00:00
coold_vm_enabled = " $( read_coolify_env COOLIFY_COOLD_VM_ENABLED true ) "
follow_dev_logs = " $( read_coolify_env COOLIFY_DEV_FOLLOW_LOGS true ) "
count = " $( coold_vm_count) "
2026-06-16 13:45:16 +00:00
while [ $# -gt 0 ] ; do
case " $1 " in
--naked)
naked = true
shift
; ;
*)
spin_args += ( " $1 " )
shift
; ;
esac
done
2026-06-16 09:39:36 +00:00
if [ " $coold_vm_enabled " != "false" ] ; then
echo " ==> Starting ${ count } Coolify coold VM(s) before Spin... "
for index in $( seq 1 " $count " ) ; do
2026-06-16 11:57:42 +00:00
coold_vm " $index " up
2026-06-16 09:39:36 +00:00
done
else
echo "==> COOLIFY_COOLD_VM_ENABLED=false; skipping coold VM."
fi
echo "==> Starting Coolify Docker stack with Spin..."
2026-06-16 13:45:16 +00:00
if [ " ${# spin_args [@] } " -gt 0 ] ; then
spin up -d " ${ spin_args [@] } "
else
spin up -d
fi
if [ " $naked " = "true" ] ; then
echo "==> --naked enabled. Skipping coolify bootstrap and Flux VM wiring. Use /v5 to bootstrap hosts from the UI."
return
fi
2026-06-16 09:39:36 +00:00
if [ " $coold_vm_enabled " != "false" ] ; then
2026-06-16 11:57:42 +00:00
echo "==> Bootstrapping coold VM mesh with coolify..."
2026-06-16 13:45:16 +00:00
coolify_bootstrap_with_retry
2026-06-16 09:39:36 +00:00
for index in $( seq 1 " $count " ) ; do
2026-06-16 10:33:26 +00:00
configure_flux_dev_for_vm " $index "
2026-06-16 09:39:36 +00:00
done
2026-06-16 13:45:16 +00:00
sync_v5_dev_lima_servers
2026-06-16 09:39:36 +00:00
fi
if [ " $follow_dev_logs " = "false" ] ; then
echo "==> Dev environment is ready. Use 'spin logs -f' or 'scripts/coold-vm.sh logs-agent' to follow logs."
return
fi
follow_logs " $coold_vm_enabled "
}
down( ) {
local coold_vm_enabled
local stop_coold_vm
2026-06-16 13:45:16 +00:00
local cleanup = false
local spin_args = ( )
2026-06-16 09:39:36 +00:00
coold_vm_enabled = " $( read_coolify_env COOLIFY_COOLD_VM_ENABLED true ) "
stop_coold_vm = " $( read_coolify_env COOLIFY_COOLD_VM_STOP_ON_DOWN false ) "
2026-06-16 13:45:16 +00:00
while [ $# -gt 0 ] ; do
case " $1 " in
--cleanup)
cleanup = true
shift
; ;
*)
spin_args += ( " $1 " )
shift
; ;
esac
done
2026-06-16 09:39:36 +00:00
if [ " $coold_vm_enabled " != "false" ] ; then
for index in $( seq 1 " $( coold_vm_count) " ) ; do
echo " ==> Stopping coold VM agent service on $( coold_vm_instance " $index " ) ... "
coold_vm " $index " stop-agent || true
done
fi
echo "==> Stopping Coolify Docker stack with Spin..."
2026-06-16 13:45:16 +00:00
if [ " ${# spin_args [@] } " -gt 0 ] ; then
spin down " ${ spin_args [@] } "
else
spin down
fi
if [ " $cleanup " = "true" ] ; then
clean_vms
return
fi
2026-06-16 09:39:36 +00:00
if [ " $stop_coold_vm " = "true" ] ; then
echo "==> Stopping Coolify coold VM..."
for index in $( seq 1 " $( coold_vm_count) " ) ; do
coold_vm " $index " stop
done
fi
}
2026-06-16 10:33:26 +00:00
clean_vms( ) {
local count
count = " $( coold_vm_count) "
echo " ==> Deleting ${ count } coold Lima VM(s). This removes VM-local state. "
for index in $( seq 1 " $count " ) ; do
instance = " $( coold_vm_instance " $index " ) "
echo " ==> Deleting ${ instance } ... "
limactl stop --force --tty= false " $instance " >/dev/null 2>& 1 || true
if ! run_with_timeout 60 limactl delete --force --tty= false " $instance " ; then
echo " WARN: limactl delete timed out for ${ instance } ; killing matching limactl clients. " >& 2
pkill -f " limactl.* ${ instance } " >/dev/null 2>& 1 || true
rm -rf " $HOME /.lima/ ${ instance } "
fi
done
}
run_with_timeout( ) {
local timeout_seconds = " $1 "
shift
local pid
local elapsed = 0
" $@ " &
pid = " $! "
while kill -0 " $pid " >/dev/null 2>& 1; do
if [ " $elapsed " -ge " $timeout_seconds " ] ; then
kill " $pid " >/dev/null 2>& 1 || true
sleep 2
kill -9 " $pid " >/dev/null 2>& 1 || true
wait " $pid " >/dev/null 2>& 1 || true
return 124
fi
sleep 1
elapsed = $(( elapsed + 1 ))
done
wait " $pid "
}
2026-06-16 09:39:36 +00:00
corrosion_for_each_vm( ) {
local label = " $1 "
shift
local count
local script
count = " $( coold_vm_count) "
script = " $( cat) "
for index in $( seq 1 " $count " ) ; do
instance = " $( coold_vm_instance " $index " ) "
echo " --- ${ instance } : ${ label } --- "
printf '%s\n' " $script " | COOLIFY_COOLD_LIMA_INSTANCE = " $instance " scripts/coold-vm.sh shell " $@ "
done
}
corrosion_check( ) {
local count
count = " $( coold_vm_count) "
for index in $( seq 1 " $count " ) ; do
instance = " $( coold_vm_instance " $index " ) "
peer_index = 1
if [ " $index " = "1" ] ; then
peer_index = 2
fi
peer_ip = " $( coold_vm_wg_ip " $peer_index " ) "
echo " --- ${ instance } : check --- "
COOLIFY_COOLD_LIMA_INSTANCE = " $instance " scripts/coold-vm.sh shell <<SH
set -e
printf 'services: '
systemctl is-active corrosion.service || true
printf 'coold: '
systemctl is-active coold.service || true
printf 'wireguard: '
sudo wg show wg0 >/dev/null 2>& 1 && echo active || echo unavailable
printf 'peer ping (${peer_ip}): '
ping -c 1 -W 2 ${ peer_ip } >/dev/null 2>& 1 && echo ok || echo failed
printf 'gossip: '
awk '/^\[gossip\]/{section=1; next} /^\[/{section=0} section && /^addr =|^bootstrap =/ {printf "%s ", \$0} END {print ""}' /etc/corrosion/config.toml
printf 'registered containers: '
sudo sqlite3 /var/lib/corrosion/corrosion.db 'select count(*) from service_endpoints;' 2>/dev/null || echo unavailable
SH
done
}
corrosion_containers( ) {
corrosion_for_each_vm containers <<'SH'
echo '[corrosion service_endpoints]'
sudo sqlite3 -header -column /var/lib/corrosion/corrosion.db \
'select container_id, container_name, namespace, host_mgmt_ip, container_ip, state, health, updated_at from service_endpoints order by updated_at desc;' 2>/dev/null \
|| echo 'service_endpoints table unavailable'
echo
echo '[rootful podman]'
sudo podman ps --format 'table {{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Status}}' 2>/dev/null \
|| echo 'rootful podman unavailable'
echo
echo '[rootless podman]'
podman ps --format 'table {{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Status}}' 2>/dev/null \
|| echo 'rootless podman unavailable'
SH
}
corrosion_config( ) {
corrosion_for_each_vm config <<'SH'
sudo cat /etc/corrosion/config.toml
SH
}
corrosion_logs( ) {
local index = " ${ 1 :- 1 } "
coold_vm " $index " logs-agent
}
corrosion_sql( ) {
local query = " $* "
if [ -z " $query " ] ; then
echo "Usage: scripts/dev.sh corrosion sql <query>" >& 2
exit 1
fi
corrosion_for_each_vm sql <<SH
sudo sqlite3 -header -column /var/lib/corrosion/corrosion.db $( printf '%q' " $query " )
SH
}
corrosion( ) {
local command = " ${ 1 :- help } "
if [ $# -gt 0 ] ; then
shift
fi
case " $command " in
check)
corrosion_check
; ;
containers| registered-containers)
corrosion_containers
; ;
config)
corrosion_config
; ;
logs)
corrosion_logs " ${ 1 :- 1 } "
; ;
sql)
corrosion_sql " $@ "
; ;
-h| --help| help )
cat <<'USAGE'
Usage: scripts/dev.sh corrosion <command>
Commands:
check Show Corrosion service, WireGuard, gossip, and row-count state
containers List registered service_endpoints rows on each VM
config Print /etc/corrosion/config.toml from each VM
logs [ n] Follow coold/corrosion logs for VM n ( default: 1)
sql <query> Run a read-only sqlite query against each Corrosion DB
USAGE
; ;
*)
echo " unknown corrosion command: $command " >& 2
echo "Run: scripts/dev.sh corrosion help" >& 2
exit 1
; ;
esac
}
example_nginx_name( ) {
local index = " $1 "
if [ " $index " = "1" ] ; then
printf '%s\n' coolify-example-nginx
return
fi
printf 'coolify-example-nginx-%s\n' " $index "
}
example_nginx_up( ) {
local count
count = " $( coold_vm_count) "
for index in $( seq 1 " $count " ) ; do
instance = " $( coold_vm_instance " $index " ) "
name = " $( example_nginx_name " $index " ) "
gateway = " $( coold_vm_container_gateway " $index " ) "
echo " --- ${ instance } : starting ${ name } with coold DNS ( ${ gateway } ) --- "
COOLIFY_COOLD_LIMA_INSTANCE = " $instance " scripts/coold-vm.sh shell <<SH
set -e
sudo podman rm -f ${ name } >/dev/null 2>& 1 || true
sudo podman run -d \\
--name ${ name } \\
--network coolify-default-mesh \\
--dns ${ gateway } \\
--dns-search default.coolify.internal \\
docker.io/library/nginx:alpine
SH
done
}
example_nginx_down( ) {
local count
count = " $( coold_vm_count) "
for index in $( seq 1 " $count " ) ; do
instance = " $( coold_vm_instance " $index " ) "
name = " $( example_nginx_name " $index " ) "
echo " --- ${ instance } : removing ${ name } --- "
COOLIFY_COOLD_LIMA_INSTANCE = " $instance " scripts/coold-vm.sh shell <<SH
sudo podman rm -f ${ name } >/dev/null 2>& 1 || true
SH
done
}
example_nginx_check_dns( ) {
COOLIFY_COOLD_LIMA_INSTANCE = " $( coold_vm_instance 1) " scripts/coold-vm.sh shell <<'SH'
set -e
echo '--- resolv.conf ---'
sudo podman exec coolify-example-nginx cat /etc/resolv.conf
echo
echo '--- coold DNS lookup through search domain ---'
sudo podman exec coolify-example-nginx nslookup coolify-example-nginx-2
echo
echo '--- coold DNS lookup by full name ---'
sudo podman exec coolify-example-nginx nslookup coolify-example-nginx-2.default.coolify.internal
SH
}
2026-06-16 14:47:10 +00:00
example_nginx_require_pair( ) {
if [ " $( coold_vm_count) " = "1" ] ; then
echo "ERROR: example-nginx ping/firewall commands require COOLIFY_COOLD_VM_COUNT=2." >& 2
exit 1
fi
}
example_nginx_container_ip( ) {
local index = " $1 "
local name
name = " $( example_nginx_name " $index " ) "
COOLIFY_COOLD_LIMA_INSTANCE = " $( coold_vm_instance " $index " ) " scripts/coold-vm.sh shell <<SH
set -e
sudo podman inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ${ name }
SH
}
example_nginx_ping( ) {
example_nginx_require_pair
COOLIFY_COOLD_LIMA_INSTANCE = " $( coold_vm_instance 1) " scripts/coold-vm.sh shell <<'SH'
set -e
sudo podman exec coolify-example-nginx wget -qO- --timeout= 3 http://coolify-example-nginx-2.default.coolify.internal/ >/dev/null
echo 'ok: coolify-example-nginx can reach coolify-example-nginx-2 on tcp/80'
SH
}
example_nginx_firewall_up( ) {
local src
local dst
example_nginx_require_pair
src = " $( example_nginx_container_ip 1) "
dst = " $( example_nginx_container_ip 2) "
scripts/dev.sh firewall allow " $src " " $dst " tcp 80
}
example_nginx_firewall_down( ) {
local src
local dst
example_nginx_require_pair
src = " $( example_nginx_container_ip 1) "
dst = " $( example_nginx_container_ip 2) "
scripts/dev.sh firewall revoke " $src " " $dst " tcp 80
}
2026-06-16 09:39:36 +00:00
example_nginx_help( ) {
cat <<'USAGE'
Usage: scripts/dev.sh example-nginx <command>
Commands:
2026-06-16 14:47:10 +00:00
up Start one nginx container on each coold VM with coold DNS configured
down Remove the example nginx containers
check-dns Verify host 1 nginx can resolve host 2 nginx through coold DNS
ping Verify host 1 nginx can reach host 2 nginx on tcp/80
firewall up Allow host 1 nginx to reach host 2 nginx through the coolify CLI
firewall down Revoke the example nginx tcp/80 allow rule through the coolify CLI
2026-06-16 09:39:36 +00:00
USAGE
}
example_nginx( ) {
local command = " ${ 1 :- help } "
if [ $# -gt 0 ] ; then
shift
fi
case " $command " in
up)
example_nginx_up
; ;
down)
example_nginx_down
; ;
check-dns)
example_nginx_check_dns
; ;
2026-06-16 14:47:10 +00:00
ping)
example_nginx_ping
; ;
firewall)
case " ${ 1 :- help } " in
up)
example_nginx_firewall_up
; ;
down)
example_nginx_firewall_down
; ;
*)
echo " unknown example-nginx firewall command: ${ 1 :- help } " >& 2
echo "Run: scripts/dev.sh example-nginx help" >& 2
exit 1
; ;
esac
; ;
2026-06-16 09:39:36 +00:00
-h| --help| help )
example_nginx_help
; ;
*)
echo " unknown example-nginx command: $command " >& 2
echo "Run: scripts/dev.sh example-nginx help" >& 2
exit 1
; ;
esac
}
firewall_help( ) {
cat <<'USAGE'
Usage: scripts/dev.sh firewall <command>
Commands:
2026-06-16 13:45:16 +00:00
allow <src> <dst> [ proto] [ port] Allow traffic through the coolify CLI ( proto/port optional)
2026-06-16 09:39:36 +00:00
revoke [ id| src] [ dst] [ proto] [ port]
2026-06-16 13:45:16 +00:00
Remove an allow rule through the coolify CLI
list List allow rules through the coolify CLI
containers List registered containers through the coolify CLI
2026-06-16 09:39:36 +00:00
Examples:
scripts/dev.sh firewall allow 10.210.0.2 10.210.1.2 tcp 80
scripts/dev.sh firewall revoke
scripts/dev.sh firewall revoke 10.210.0.2 10.210.1.2 tcp 80
scripts/dev.sh firewall revoke 3ba6e0c235a6
scripts/dev.sh firewall list
USAGE
}
2026-06-16 13:45:16 +00:00
coolify_firewall( ) {
local command = " $1 "
shift
local nodes
local ssh_config
2026-06-16 09:39:36 +00:00
2026-06-16 13:45:16 +00:00
ensure_coolify
nodes = " $( coolify_nodes_arg) " || return 1
ssh_config = " $( lima_ssh_config) " || return 1
" $( coolify_cli_bin) " firewall " $command " \
--nodes " $nodes " \
--ssh-config " $ssh_config " \
--ssh-user " $( coolify_ssh_user) " \
" $@ "
2026-06-16 09:39:36 +00:00
}
firewall_allow( ) {
local src = " ${ 1 :- } "
local dst = " ${ 2 :- } "
local proto = " ${ 3 :- } "
local port = " ${ 4 :- } "
2026-06-16 13:45:16 +00:00
local args = ( )
2026-06-16 09:39:36 +00:00
if [ -z " $src " ] || [ -z " $dst " ] ; then
firewall_help >& 2
exit 1
fi
if [ -n " $port " ] && [ -z " $proto " ] ; then
echo "ERROR: port requires proto (tcp or udp)." >& 2
exit 1
fi
2026-06-16 13:45:16 +00:00
args += ( --from " $src " --to " $dst " )
2026-06-16 09:39:36 +00:00
if [ -n " $proto " ] ; then
2026-06-16 13:45:16 +00:00
args += ( --proto " $proto " )
2026-06-16 09:39:36 +00:00
fi
if [ -n " $port " ] ; then
2026-06-16 13:45:16 +00:00
args += ( --port " $port " )
2026-06-16 09:39:36 +00:00
fi
2026-06-16 13:45:16 +00:00
coolify_firewall allow " ${ args [@] } "
2026-06-16 09:39:36 +00:00
}
firewall_revoke( ) {
local id_or_src = " ${ 1 :- } "
local dst = " ${ 2 :- } "
local proto = " ${ 3 :- } "
local port = " ${ 4 :- } "
2026-06-16 13:45:16 +00:00
local args = ( )
2026-06-16 09:39:36 +00:00
if [ -z " $id_or_src " ] ; then
echo "Current firewall allow rule IDs:"
firewall_list
echo
echo "Revoke one with: scripts/dev.sh firewall revoke <id>"
return
fi
if [ -z " $dst " ] ; then
2026-06-16 13:45:16 +00:00
args += ( --id " $id_or_src " )
2026-06-16 09:39:36 +00:00
else
2026-06-16 13:45:16 +00:00
args += ( --from " $id_or_src " --to " $dst " )
if [ -n " $proto " ] ; then
args += ( --proto " $proto " )
fi
if [ -n " $port " ] ; then
args += ( --port " $port " )
fi
2026-06-16 09:39:36 +00:00
fi
2026-06-16 13:45:16 +00:00
coolify_firewall revoke " ${ args [@] } "
2026-06-16 09:39:36 +00:00
}
firewall_list( ) {
2026-06-16 13:45:16 +00:00
coolify_firewall list " $@ "
2026-06-16 09:39:36 +00:00
}
2026-06-16 13:45:16 +00:00
firewall_containers( ) {
coolify_firewall containers " $@ "
2026-06-16 09:39:36 +00:00
}
firewall( ) {
local command = " ${ 1 :- help } "
if [ $# -gt 0 ] ; then
shift
fi
case " $command " in
allow)
firewall_allow " $@ "
; ;
revoke| remove| delete| deny)
firewall_revoke " $@ "
; ;
list)
2026-06-16 13:45:16 +00:00
firewall_list " $@ "
2026-06-16 09:39:36 +00:00
; ;
2026-06-16 13:45:16 +00:00
containers)
firewall_containers " $@ "
2026-06-16 09:39:36 +00:00
; ;
-h| --help| help )
firewall_help
; ;
*)
echo " unknown firewall command: $command " >& 2
echo "Run: scripts/dev.sh firewall help" >& 2
exit 1
; ;
esac
}
usage( ) {
cat <<'USAGE'
Usage: scripts/dev.sh <command> [ spin args]
Commands:
up Start the coold VM, Spin stack, and dev coold agent
2026-06-16 13:45:16 +00:00
up --naked
Start the coold VM( s) and Spin stack only; skip host bootstrap so /v5 can bootstrap
2026-06-16 09:39:36 +00:00
down Stop the dev coold agent and Spin stack
2026-06-16 13:45:16 +00:00
down --cleanup
Stop the dev stack, then delete the coold Lima VM( s) and VM-local state
2026-06-16 09:39:36 +00:00
shell [ n] Open a shell inside coold VM n ( default: 1)
list Show Lima instances
2026-06-16 13:45:16 +00:00
clean-vms Delete the coold Lima VMs and all VM-local runtime state ( alias for down --cleanup)
2026-06-16 09:39:36 +00:00
corrosion <command> Inspect Corrosion state, config, logs, and registered containers
firewall <command> Manage dev coold firewall allow rules
example-nginx <command> Start/check example nginx containers with coold DNS
2026-06-16 11:57:42 +00:00
coolify <command> Install/run the released coolify dev helper
2026-06-16 09:39:36 +00:00
USAGE
}
cmd = " ${ 1 :- } "
if [ $# -gt 0 ] ; then
shift
fi
case " $cmd " in
up)
up " $@ "
; ;
down)
down " $@ "
; ;
shell)
coold_vm " ${ 1 :- 1 } " shell
; ;
list)
limactl list
; ;
2026-06-16 10:33:26 +00:00
clean-vms| clean-vm| reset-vms)
2026-06-16 13:45:16 +00:00
down --cleanup
2026-06-16 10:33:26 +00:00
; ;
2026-06-16 09:39:36 +00:00
corrosion)
corrosion " $@ "
; ;
firewall)
firewall " $@ "
; ;
example-nginx)
example_nginx " $@ "
; ;
2026-06-16 11:57:42 +00:00
coolify)
coolify_dev " $@ "
2026-06-16 10:33:26 +00:00
; ;
2026-06-16 09:39:36 +00:00
-h| --help| help| "" )
usage
; ;
*)
echo " unknown command: $cmd " >& 2
usage >& 2
exit 1
; ;
esac