2026-06-16 09:39:36 +00:00
#!/usr/bin/env bash
set -euo pipefail
ROOT = " $( cd " $( dirname " ${ BASH_SOURCE [0] } " ) /.. " && pwd ) "
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 " $ROOT /.env " ] ; then
env_value = " $( grep -E " ^ ${ key } = " " $ROOT /.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 "
}
INSTANCE = " $( read_coolify_env COOLIFY_COOLD_LIMA_INSTANCE coold-dev) "
VERSION = " $( read_coolify_env COOLIFY_COOLD_VERSION nightly) "
CORROSION_VERSION = " $( read_coolify_env COOLIFY_CORROSION_VERSION v1.0.0) "
FLUX_URL = " $( read_coolify_env COOLIFY_COOLD_VM_FLUX_URL http://host.lima.internal:6443) "
2026-06-19 09:44:42 +00:00
START_TIMEOUT = " $( read_coolify_env COOLIFY_COOLD_VM_START_TIMEOUT 300) "
2026-06-16 09:39:36 +00:00
WG_IP = " $( read_coolify_env COOLIFY_COOLD_VM_WG_IP "" ) "
WG_PEER_IP = " $( read_coolify_env COOLIFY_COOLD_VM_WG_PEER_IP "" ) "
WG_PEER_ENDPOINT = " $( read_coolify_env COOLIFY_COOLD_VM_WG_PEER_ENDPOINT "" ) "
WG_PEER_PUBLIC_KEY = " $( read_coolify_env COOLIFY_COOLD_VM_WG_PEER_PUBLIC_KEY "" ) "
CONTAINER_SUBNET = " $( read_coolify_env COOLIFY_COOLD_VM_CONTAINER_SUBNET 10.210.0.0/24) "
CONTAINER_GATEWAY = " $( read_coolify_env COOLIFY_COOLD_VM_CONTAINER_GATEWAY 10.210.0.1) "
TEMPLATE = " $ROOT /dev/lima/coold.yaml "
GUEST_COOLIFY_ROOT = "/workspace/coolify"
usage( ) {
cat <<USAGE
Usage: scripts/coold-vm.sh <command>
Commands:
2026-06-16 10:33:26 +00:00
up Create/start the Lima VM with minimal server prerequisites
2026-06-16 09:39:36 +00:00
dev Start packaged coold + Corrosion inside the VM
start-agent
Start production-like coold.service + corrosion.service inside the VM
stop-agent
Stop the VM coold.service + corrosion.service
logs-agent
Follow the VM coold.service + corrosion.service logs
install-host-jwt [ token]
Install a Flux host JWT into the VM at /etc/coolify/host-jwt
shell Open a shell inside the VM
status Show Lima instance status
stop Stop the VM
delete Delete the VM and all VM-local runtime state
Environment:
COOLIFY_COOLD_LIMA_INSTANCE Override Lima instance name ( default: coold-dev)
COOLIFY_COOLD_VERSION coold release tag to install ( default: nightly)
COOLIFY_CORROSION_VERSION corrosion release tag to install ( default: v1.0.0)
COOLIFY_COOLD_VM_FLUX_URL Flux gRPC URL visible from the VM ( default: http://host.lima.internal:6443)
2026-06-19 09:44:42 +00:00
COOLIFY_COOLD_VM_START_TIMEOUT Seconds to wait for Lima SSH/provisioning ( default: 300)
2026-06-16 09:39:36 +00:00
COOLIFY_COOLD_VM_WG_IP Optional WireGuard mgmt IP for this host
COOLIFY_COOLD_VM_CONTAINER_SUBNET Podman mesh subnet for this host
COOLIFY_COOLD_VM_CONTAINER_GATEWAY Podman mesh gateway for this host
Guest mounts:
$ROOT -> $GUEST_COOLIFY_ROOT
Installed from:
https://github.com/coollabsio/coold/releases/tag/$VERSION
https://github.com/superfly/corrosion/releases/tag/$CORROSION_VERSION
USAGE
}
require_lima( ) {
command -v limactl >/dev/null 2>& 1 || {
echo "limactl is required. Install Lima first: brew install lima" >& 2
exit 1
}
}
instance_exists( ) {
limactl list 2>/dev/null | awk 'NR > 1 {print $1}' | grep -qx " $INSTANCE "
}
instance_running( ) {
limactl list 2>/dev/null | awk -v name = " $INSTANCE " 'NR > 1 && $1 == name {print $2}' | grep -qx Running
}
lima_shell( ) {
( cd /tmp && limactl shell " $INSTANCE " -- " $@ " )
}
2026-06-19 09:44:42 +00:00
kill_matching_processes( ) {
local pattern = " $1 "
local pids
pids = " $( pgrep -f " $pattern " 2>/dev/null || true ) "
if [ -z " $pids " ] ; then
return
fi
kill $pids >/dev/null 2>& 1 || true
sleep 1
kill -9 $pids >/dev/null 2>& 1 || true
}
cleanup_lima_probe_processes( ) {
kill_matching_processes " limactl shell ${ INSTANCE } -- true "
kill_matching_processes " ssh .*ControlPath=.* ${ INSTANCE } /ssh.sock "
kill_matching_processes " ssh: .*/.lima/ ${ INSTANCE } /ssh.sock "
rm -f " $HOME /.lima/ ${ INSTANCE } /ssh.sock "
}
cleanup_lima_hostagent_processes( ) {
kill_matching_processes " limactl hostagent .* ${ INSTANCE } "
rm -f " $HOME /.lima/ ${ INSTANCE } /ha.sock "
}
lima_shell_timeout( ) {
local timeout_seconds = " $1 "
shift
local pid
local elapsed = 0
lima_shell " $@ " &
pid = " $! "
while kill -0 " $pid " >/dev/null 2>& 1; do
if [ " $elapsed " -ge " $timeout_seconds " ] ; then
pkill -P " $pid " >/dev/null 2>& 1 || true
kill " $pid " >/dev/null 2>& 1 || true
sleep 1
pkill -P " $pid " >/dev/null 2>& 1 || true
kill -9 " $pid " >/dev/null 2>& 1 || true
wait " $pid " >/dev/null 2>& 1 || true
cleanup_lima_probe_processes
return 124
fi
sleep 1
elapsed = $(( elapsed + 1 ))
done
wait " $pid "
}
2026-06-16 09:39:36 +00:00
vm_primary_ip( ) {
lima_shell sh -lc "ip -4 route get 1.1.1.1 | awk '{print \$7; exit}'"
}
wireguard_public_key( ) {
lima_shell sudo sh -lc 'install -d -m 0700 /etc/wireguard; if [ ! -s /etc/wireguard/privatekey ]; then wg genkey | tee /etc/wireguard/privatekey | wg pubkey > /etc/wireguard/publickey; chmod 600 /etc/wireguard/privatekey; fi; cat /etc/wireguard/publickey'
}
setup_wireguard( ) {
local ip = " ${ 1 :- $WG_IP } "
local peer_ip = " ${ 2 :- $WG_PEER_IP } "
local peer_endpoint = " ${ 3 :- $WG_PEER_ENDPOINT } "
local peer_public_key = " ${ 4 :- $WG_PEER_PUBLIC_KEY } "
local listen_port = " ${ 5 :- 51820 } "
local peer_port = " ${ 6 :- 51820 } "
local peer_subnet = " ${ 7 :- } "
if [ -z " $ip " ] ; then
echo "ERROR: WireGuard IP is required." >& 2
exit 1
fi
wireguard_public_key >/dev/null
if [ -n " $peer_ip " ] && [ -n " $peer_endpoint " ] && [ -n " $peer_public_key " ] ; then
lima_shell sudo sh -lc " cat >/etc/wireguard/wg0.conf.tmp <<WG
[ Interface]
Address = ${ ip } /32
ListenPort = ${ listen_port }
PrivateKey = \$ ( cat /etc/wireguard/privatekey)
[ Peer]
PublicKey = ${ peer_public_key }
AllowedIPs = ${ peer_ip } /32${ peer_subnet : +, ${ peer_subnet } }
Endpoint = ${ peer_endpoint } :${ peer_port }
PersistentKeepalive = 5
WG
chmod 600 /etc/wireguard/wg0.conf.tmp && mv /etc/wireguard/wg0.conf.tmp /etc/wireguard/wg0.conf && wg-quick down wg0 >/dev/null 2>& 1 || true; wg-quick up wg0"
else
lima_shell sudo sh -lc " cat >/etc/wireguard/wg0.conf.tmp <<WG
[ Interface]
Address = ${ ip } /32
ListenPort = ${ listen_port }
PrivateKey = \$ ( cat /etc/wireguard/privatekey)
WG
chmod 600 /etc/wireguard/wg0.conf.tmp && mv /etc/wireguard/wg0.conf.tmp /etc/wireguard/wg0.conf && wg-quick down wg0 >/dev/null 2>& 1 || true; wg-quick up wg0"
fi
}
install_host_jwt( ) {
token = " ${ 1 :- } "
if [ -z " $token " ] ; then
token = " $( cat) "
fi
if [ -z " $token " ] ; then
echo "ERROR: host JWT is empty." >& 2
exit 1
fi
printf '%s\n' " $token " | lima_shell sudo sh -c 'install -d -m 0755 /etc/coolify && cat > /tmp/coolify-host-jwt && install -m 0600 /tmp/coolify-host-jwt /etc/coolify/host-jwt && rm -f /tmp/coolify-host-jwt'
}
stop_agent_processes( ) {
lima_shell sudo systemctl stop coold.service corrosion.service coold-dev-agent.service >/dev/null 2>& 1 || true
lima_shell sudo pkill -x coold >/dev/null 2>& 1 || true
lima_shell sudo pkill -x corrosion >/dev/null 2>& 1 || true
}
ensure_podman_networks( ) {
local current_subnet
current_subnet = " $( lima_shell sudo podman network inspect coolify-default-mesh --format '{{range .Subnets}}{{.Subnet}}{{end}}' 2>/dev/null || true ) "
if [ -n " $current_subnet " ] && [ " $current_subnet " != " $CONTAINER_SUBNET " ] ; then
lima_shell sudo podman network rm coolify-default-mesh >/dev/null
current_subnet = ""
fi
if [ -z " $current_subnet " ] ; then
lima_shell sudo podman network create --subnet " $CONTAINER_SUBNET " --gateway " $CONTAINER_GATEWAY " coolify-default-mesh >/dev/null
fi
}
2026-06-20 07:23:16 +00:00
ensure_mesh_dns_anchor( ) {
lima_shell sudo podman run -d --replace \
--name coolify-v5-mesh-dns-anchor \
--network coolify-default-mesh \
docker.io/library/alpine:3.20 \
sleep infinity >/dev/null
}
configure_system_resolved( ) {
lima_shell sudo rm -f /etc/systemd/resolved.conf.d/coolify-internal.conf
lima_shell sudo systemctl restart systemd-resolved.service
lima_shell sudo resolvectl dns podman1 " $CONTAINER_GATEWAY "
lima_shell sudo resolvectl domain podman1 '~coolify.internal'
lima_shell sudo resolvectl default-route podman1 false
}
2026-06-16 09:39:36 +00:00
write_runtime_config( ) {
local gossip_addr = "127.0.0.1:8787"
local bootstrap = ""
if [ -n " $WG_IP " ] ; then
gossip_addr = " $WG_IP :8787 "
fi
if [ -n " $WG_PEER_IP " ] ; then
bootstrap = " \" $WG_PEER_IP :8787\" "
fi
2026-06-21 14:54:02 +00:00
lima_shell sudo install -d -m 0755 /etc/corrosion/schemas /etc/coolify /run/coolify /var/lib/corrosion /var/run/corrosion /var/lib/coolify-dev
2026-06-16 09:39:36 +00:00
lima_shell sudo tee /etc/corrosion/schemas/coolify.sql >/dev/null <<'SQL'
CREATE TABLE service_endpoints (
container_id TEXT NOT NULL DEFAULT '' PRIMARY KEY,
container_name TEXT NOT NULL DEFAULT '' ,
namespace TEXT NOT NULL DEFAULT '' ,
host_mgmt_ip TEXT NOT NULL DEFAULT '' ,
container_ip TEXT NOT NULL DEFAULT '' ,
state TEXT NOT NULL DEFAULT '' ,
health TEXT NOT NULL DEFAULT 'unknown' ,
updated_at INTEGER NOT NULL DEFAULT 0
) ;
SQL
lima_shell sudo tee /etc/corrosion/config.toml >/dev/null <<TOML
[ db]
path = "/var/lib/corrosion/corrosion.db"
schema_paths = [ "/etc/corrosion/schemas" ]
[ gossip]
addr = " $gossip_addr "
bootstrap = [ $bootstrap ]
plaintext = true
[ api]
addr = "127.0.0.1:8080"
[ admin]
path = "/var/run/corrosion/admin.sock"
TOML
}
run_foreground( ) {
stop_agent_processes
write_runtime_config
ensure_podman_networks
2026-06-20 07:23:16 +00:00
configure_system_resolved
ensure_mesh_dns_anchor
2026-06-16 09:39:36 +00:00
install_mesh_firewall
( cd /tmp && limactl shell " $INSTANCE " -- sudo \
env COOLIFY_COOLD_HOST_MGMT_IP = " ${ WG_IP :- 127 .0.0.1 } " \
COOLIFY_COOLD_FLUX_URL = " $FLUX_URL " \
CONTAINER_GATEWAY = " $CONTAINER_GATEWAY " \
bash -s) <<'RUNNER '
set -euo pipefail
echo " coold: $( /usr/local/bin/coold --version) "
echo " corrosion: $( /usr/local/bin/corrosion --version 2>/dev/null || cat /usr/local/bin/corrosion.version) "
echo "starting packaged coold endpoint with corrosion in local mode"
cleanup( ) {
jobs -pr | xargs -r kill || true
}
trap cleanup EXIT INT TERM
/usr/local/bin/corrosion agent --config /etc/corrosion/config.toml &
COOLIFY_COOLD_HOST_MGMT_IP = " ${ COOLIFY_COOLD_HOST_MGMT_IP :- 127 .0.0.1 } " \
COOLIFY_COOLD_PODMAN_SOCKET = " ${ COOLIFY_COOLD_PODMAN_SOCKET :- /run/podman/podman.sock } " \
COOLIFY_COOLD_CORROSION_URL = " ${ COOLIFY_COOLD_CORROSION_URL :- http : //127.0.0.1 : 8080 } " \
COOLIFY_COOLD_NAMESPACES = " ${ COOLIFY_COOLD_NAMESPACES :- default : coolify -default-mesh : $CONTAINER_GATEWAY } " \
COOLIFY_COOLD_DNS_ZONE = " ${ COOLIFY_COOLD_DNS_ZONE :- coolify .internal } " \
COOLIFY_COOLD_FLUX_URL = " ${ COOLIFY_COOLD_FLUX_URL :- http : //host.lima.internal : 6443 } " \
COOLIFY_COOLD_HOST_JWT_PATH = " ${ COOLIFY_COOLD_HOST_JWT_PATH :- /etc/coolify/host-jwt } " \
/usr/local/bin/coold &
wait
RUNNER
}
install_mesh_firewall( ) {
lima_shell sudo install -d -m 0755 /etc/coolify
lima_shell sudo touch /etc/coolify/allow.rules /etc/coolify/allow.nft
lima_shell sudo tee /etc/coolify/bridge-fw.nft >/dev/null <<NFT
add table bridge coolify_bridge
add chain bridge coolify_bridge coolify_allow
flush chain bridge coolify_bridge coolify_allow
add chain bridge coolify_bridge coolify_intra
flush chain bridge coolify_bridge coolify_intra
add rule bridge coolify_bridge coolify_intra jump coolify_allow
add rule bridge coolify_bridge coolify_intra drop
add chain bridge coolify_bridge forward { type filter hook forward priority -200; policy accept; }
flush chain bridge coolify_bridge forward
add rule bridge coolify_bridge forward meta protocol != ip accept
add rule bridge coolify_bridge forward ct state established,related accept
add rule bridge coolify_bridge forward ip saddr { $CONTAINER_SUBNET } jump coolify_intra
add rule bridge coolify_bridge forward ip daddr { $CONTAINER_SUBNET } jump coolify_intra
NFT
lima_shell sudo tee /etc/systemd/system/coolify-mesh-fw.service >/dev/null <<UNIT
[ Unit]
Description = Coolify mesh firewall rules
After = network-online.target
Wants = network-online.target
[ Service]
Type = oneshot
RemainAfterExit = yes
ExecStart = /usr/sbin/sysctl -w net.ipv4.ip_forward= 1
ExecStart = /bin/sh -c " /usr/sbin/iptables -t nat -C POSTROUTING -s $CONTAINER_SUBNET -o wg0 -j RETURN 2>/dev/null || /usr/sbin/iptables -t nat -I POSTROUTING -s $CONTAINER_SUBNET -o wg0 -j RETURN "
ExecStart = /bin/sh -c " /usr/sbin/iptables -D FORWARD -s $CONTAINER_SUBNET -j ACCEPT 2>/dev/null || true "
ExecStart = /bin/sh -c " /usr/sbin/iptables -D FORWARD -d $CONTAINER_SUBNET -j ACCEPT 2>/dev/null || true "
ExecStart = /bin/sh -c "/usr/sbin/iptables -N COOLIFY-ALLOW 2>/dev/null || true"
ExecStart = /bin/sh -c "/usr/sbin/iptables -N COOLIFY-INTRA 2>/dev/null || true"
ExecStart = /usr/sbin/iptables -F COOLIFY-ALLOW
ExecStart = /usr/sbin/iptables -F COOLIFY-INTRA
ExecStart = /usr/sbin/iptables -A COOLIFY-INTRA -j COOLIFY-ALLOW
ExecStart = /usr/sbin/iptables -A COOLIFY-INTRA -j DROP
ExecStart = /bin/sh -c "/usr/sbin/iptables -C FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT 2>/dev/null || /usr/sbin/iptables -I FORWARD 1 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT"
ExecStart = /bin/sh -c " /usr/sbin/iptables -C FORWARD -d $CONTAINER_SUBNET -j COOLIFY-INTRA 2>/dev/null || /usr/sbin/iptables -A FORWARD -d $CONTAINER_SUBNET -j COOLIFY-INTRA "
ExecStart = /bin/sh -c " /usr/sbin/iptables -C FORWARD -s $CONTAINER_SUBNET -j COOLIFY-INTRA 2>/dev/null || /usr/sbin/iptables -A FORWARD -s $CONTAINER_SUBNET -j COOLIFY-INTRA "
ExecStart = /bin/sh -c "nft delete table bridge coolify_bridge 2>/dev/null || true"
ExecStart = /bin/sh -c "nft -f /etc/coolify/bridge-fw.nft"
ExecStart = /bin/sh -c "[ -s /etc/coolify/allow.nft ] && nft -f /etc/coolify/allow.nft || true"
[ Install]
WantedBy = multi-user.target
UNIT
lima_shell sudo systemctl daemon-reload
lima_shell sudo systemctl enable --now coolify-mesh-fw.service
lima_shell sudo systemctl restart coolify-mesh-fw.service
}
start_agent( ) {
stop_agent_processes
write_runtime_config
ensure_podman_networks
2026-06-20 07:23:16 +00:00
configure_system_resolved
ensure_mesh_dns_anchor
2026-06-16 09:39:36 +00:00
install_mesh_firewall
lima_shell sudo tee /etc/systemd/system/corrosion.service >/dev/null <<'UNIT'
[ Unit]
Description = Corrosion local state store
After = network-online.target
Wants = network-online.target
[ Service]
ExecStart = /usr/local/bin/corrosion agent --config /etc/corrosion/config.toml
Restart = on-failure
RestartSec = 2s
[ Install]
WantedBy = multi-user.target
UNIT
lima_shell sudo tee /etc/systemd/system/coold.service >/dev/null <<UNIT
[ Unit]
Description = Coolify host agent
Wants = corrosion.service
After = corrosion.service network-online.target podman.socket coolify-mesh-fw.service
[ Service]
Environment = COOLIFY_COOLD_HOST_MGMT_IP = ${ WG_IP :- 127 .0.0.1 }
Environment = COOLIFY_COOLD_PODMAN_SOCKET = /run/podman/podman.sock
Environment = COOLIFY_COOLD_CORROSION_URL = http://127.0.0.1:8080
Environment = COOLIFY_COOLD_NAMESPACES = default:coolify-default-mesh:$CONTAINER_GATEWAY
Environment = COOLIFY_COOLD_DNS_ZONE = coolify.internal
Environment = COOLIFY_COOLD_FLUX_URL = $FLUX_URL
Environment = COOLIFY_COOLD_HOST_JWT_PATH = /etc/coolify/host-jwt
ExecStart = /usr/local/bin/coold
AmbientCapabilities = CAP_NET_BIND_SERVICE CAP_NET_ADMIN CAP_NET_RAW
Restart = on-failure
RestartSec = 2s
[ Install]
WantedBy = multi-user.target
UNIT
lima_shell sudo systemctl daemon-reload
lima_shell sudo systemctl enable --now corrosion.service coold.service
lima_shell sudo systemctl restart corrosion.service coold.service
}
start_vm( ) {
if instance_running; then
return
fi
2026-06-19 09:44:42 +00:00
cleanup_lima_probe_processes
cleanup_lima_hostagent_processes
2026-06-16 09:39:36 +00:00
if instance_exists; then
limactl start --tty= false " $INSTANCE "
else
2026-06-21 13:13:59 +00:00
limactl start --tty= false --name= " $INSTANCE " " $TEMPLATE "
2026-06-16 09:39:36 +00:00
fi
}
latest_lima_message( ) {
log_file = " $HOME /.lima/ $INSTANCE /ha.stderr.log "
if [ ! -f " $log_file " ] ; then
echo "creating Lima instance directory"
return 0
fi
grep -E '"msg":"(Starting VZ|Waiting for|The essential requirement|Executing /mnt/lima|SSH Local Port|Port is available|Attempting|Downloaded|Using the existing instance|The instance)' " $log_file " | tail -n 1 | sed -E 's/^.*"msg":"(.*)","time":.*$/\1/' | sed 's/\\"/"/g' || true
}
wait_for_lima_start( ) {
start_vm &
start_pid = $!
elapsed = 0
while kill -0 " $start_pid " 2>/dev/null; do
2026-06-19 09:44:42 +00:00
if [ " $elapsed " -ge " $START_TIMEOUT " ] ; then
echo " ERROR: Lima start timed out after ${ START_TIMEOUT } s for ${ INSTANCE } . " >& 2
kill " $start_pid " >/dev/null 2>& 1 || true
sleep 2
kill -9 " $start_pid " >/dev/null 2>& 1 || true
wait " $start_pid " >/dev/null 2>& 1 || true
return 124
fi
if instance_exists && lima_shell_timeout 5 true >/dev/null 2>& 1; then
2026-06-16 09:39:36 +00:00
status = " $( lima_shell cloud-init status 2>/dev/null || true ) "
printf ' = = > [ %3ss] Lima start: guest SSH ready, cloud-init %s
' " $elapsed " " ${ status :- unknown } "
lima_shell sudo sh -c 'test -f /var/log/cloud-init-output.log && tail -n 12 /var/log/cloud-init-output.log || true' 2>/dev/null | awk '{ print "[guest] " $0; fflush(); }' || true
if ! printf '%s' " $status " | grep -q running; then
2026-06-16 11:57:42 +00:00
if kill -0 " $start_pid " 2>/dev/null; then
kill " $start_pid " >/dev/null 2>& 1 || true
disown " $start_pid " >/dev/null 2>& 1 || true
fi
return
2026-06-16 09:39:36 +00:00
fi
else
message = " $( latest_lima_message) "
printf ' = = > [ %3ss] Lima start: %s
' " $elapsed " " ${ message :- booting } "
fi
sleep 5
elapsed = $(( elapsed + 5 ))
done
wait " $start_pid " 2>/dev/null || true
}
wait_for_guest_provisioning( ) {
echo "==> Waiting for guest SSH..."
2026-06-19 09:44:42 +00:00
elapsed = 0
until instance_exists && lima_shell_timeout 5 true >/dev/null 2>& 1; do
if [ " $elapsed " -ge " $START_TIMEOUT " ] ; then
echo " ERROR: Guest SSH timed out after ${ START_TIMEOUT } s for ${ INSTANCE } . " >& 2
return 124
fi
2026-06-16 09:39:36 +00:00
message = " $( latest_lima_message) "
printf ' = = > Waiting for guest SSH: %s
' " ${ message :- booting } "
sleep 5
2026-06-19 09:44:42 +00:00
elapsed = $(( elapsed + 5 ))
2026-06-16 09:39:36 +00:00
done
status = " $( lima_shell cloud-init status 2>/dev/null || true ) "
if printf '%s' " $status " | grep -q running; then
echo "==> Guest SSH is ready; streaming cloud-init output until provisioning completes..."
(
lima_shell sudo sh -c 'touch /var/log/cloud-init-output.log; tail -n 40 -F /var/log/cloud-init-output.log' 2>/dev/null | awk '{ print "[guest] " $0; fflush(); }'
) &
tail_pid = $!
while true; do
2026-06-19 09:44:42 +00:00
if [ " $elapsed " -ge " $START_TIMEOUT " ] ; then
echo " ERROR: Guest provisioning timed out after ${ START_TIMEOUT } s for ${ INSTANCE } . " >& 2
kill " $tail_pid " >/dev/null 2>& 1 || true
wait " $tail_pid " 2>/dev/null || true
return 124
fi
2026-06-16 09:39:36 +00:00
status = " $( lima_shell cloud-init status 2>/dev/null || true ) "
printf ' = = > Guest cloud-init: %s
' " ${ status :- unknown } "
if ! printf '%s' " $status " | grep -q running; then
break
fi
sleep 5
2026-06-19 09:44:42 +00:00
elapsed = $(( elapsed + 5 ))
2026-06-16 09:39:36 +00:00
done
kill " $tail_pid " >/dev/null 2>& 1 || true
wait " $tail_pid " 2>/dev/null || true
else
printf ' = = > Guest cloud-init: %s
' " ${ status :- unknown } "
fi
echo "==> Final guest provisioning status:"
2026-06-16 10:33:26 +00:00
lima_shell bash -lc 'cloud-init status 2>/dev/null || true; echo "minimal VM ready"; true' \
| awk '{ print "[guest] " $0; fflush(); }' || true
2026-06-16 09:39:36 +00:00
}
2026-06-21 13:13:59 +00:00
ensure_mdns_hostname( ) {
local current_hostname
current_hostname = " $( lima_shell hostname 2>/dev/null || true ) "
if [ " $current_hostname " != " $INSTANCE " ] ; then
echo " ==> Setting guest hostname to ${ INSTANCE } for ${ INSTANCE } .local mDNS... "
lima_shell sudo hostnamectl set-hostname " $INSTANCE "
fi
lima_shell sudo systemctl restart avahi-daemon.service
}
2026-06-16 09:39:36 +00:00
up_with_logs( ) {
echo " ==> Coolify coold VM: $INSTANCE "
echo " ==> coold package tag: $VERSION "
echo " ==> corrosion package tag: $CORROSION_VERSION "
2026-06-21 13:13:59 +00:00
echo " ==> Lima config: $TEMPLATE "
2026-06-16 09:39:36 +00:00
wait_for_lima_start
wait_for_guest_provisioning
2026-06-21 13:13:59 +00:00
ensure_mdns_hostname
2026-06-16 09:39:36 +00:00
2026-06-16 11:57:42 +00:00
echo "==> VM is ready. Run coolify bootstrap via: scripts/dev.sh up"
2026-06-16 09:39:36 +00:00
}
cmd = " ${ 1 :- } "
case " $cmd " in
up)
require_lima
up_with_logs
; ;
dev)
require_lima
start_vm >/dev/null
run_foreground
; ;
start-agent)
require_lima
start_vm >/dev/null
start_agent
; ;
stop-agent)
require_lima
if instance_running; then
stop_agent_processes
fi
; ;
logs-agent)
require_lima
start_vm >/dev/null
exec bash -lc " cd /tmp && limactl shell ' $INSTANCE ' -- sudo journalctl -u coold.service -u corrosion.service -f -n 100 "
; ;
install-host-jwt)
require_lima
start_vm >/dev/null
install_host_jwt " ${ 2 :- } "
; ;
wg-public-key)
require_lima
start_vm >/dev/null
wireguard_public_key
; ;
vm-ip)
require_lima
start_vm >/dev/null
vm_primary_ip
; ;
setup-wireguard)
require_lima
start_vm >/dev/null
setup_wireguard " ${ 2 :- } " " ${ 3 :- } " " ${ 4 :- } " " ${ 5 :- } " " ${ 6 :- } " " ${ 7 :- } " " ${ 8 :- } "
; ;
shell)
require_lima
start_vm >/dev/null
2026-06-16 10:33:26 +00:00
exec bash -lc " cd /tmp && limactl shell ' $INSTANCE ' -- sudo env TERM=xterm-256color SYSTEMD_PAGER=cat SYSTEMD_LESS=FRXMK bash -l "
2026-06-16 09:39:36 +00:00
; ;
status)
require_lima
if instance_exists; then
exec limactl list " $INSTANCE "
fi
echo " No Lima instance named ' $INSTANCE ' exists yet. "
echo "Run: scripts/coold-vm.sh up"
; ;
stop)
require_lima
exec limactl stop " $INSTANCE "
; ;
delete| destroy)
require_lima
exec limactl delete --force --tty= false " $INSTANCE "
; ;
-h| --help| help| "" )
usage
; ;
*)
echo " unknown command: $cmd " >& 2
usage >& 2
exit 1
; ;
esac