coolifyv32Go

command module
v0.0.0-...-16f1f8d Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 13, 2026 License: MIT Imports: 22 Imported by: 0

README

coolfymigrater

One-shot migration tool that hands a running Coolify v3 install over to coolifygo on the same host. Reads v3's SQLite database directly, decrypts its secrets with v3's COOLIFY_SECRET_KEY, inserts equivalent rows into coolifygo's Postgres, takes over every workload container (apps + DBs), upgrades the host Docker engine, and finally wipes the v3 stack.

Quickstart — one command

On the v3 host:

curl -fsSL https://raw.githubusercontent.com/annihilatorrrr/coolifyv32Go/main/install.sh | sudo bash

That wrapper does the whole thing end to end:

  1. Installs Go if missing or older than required (Go 1.26.5+) — never downgrades a newer host toolchain
  2. go installs this migrater from source
  3. Freezes v3 — stops coolify + coolify-fluentbit, releasing port 3000 and quiescing SQLite
  4. Upgrades the host's Docker engine (always-on — v3 ships with an old version; safe here because v3 is frozen and coolifygo isn't up yet)
  5. Installs coolifygo via gocoolify/install.sh (skipped if already running) — port 3000 is now free and Docker is modern
  6. Runs --phase=pre-docker — discover, extract SQLite, decrypt, plan, insert into coolifygo's Postgres
  7. Runs --phase=post-docker — takes over every v3 workload container, then wipes v3 completely

No manual flags required — DSN + encryption key are sourced from /data/coolifygo/.env.

Install

The migrater is a single Go binary. You can install it the same way install.sh does, or build it from source.

# Install the published binary onto PATH (what install.sh runs internally)
GOBIN=/usr/local/bin go install github.com/annihilatorrrr/coolifyv32Go@latest

# …or build from a checkout of this repo
git clone https://github.com/annihilatorrrr/coolifyv32Go
cd coolifyv32Go
go build -o bin/coolfymigrater .      # binary at ./bin/coolfymigrater

Requires Go 1.26.5+ and a local Docker socket. No CGO — the SQLite reader is pure Go (modernc.org/sqlite).

Usage

coolfymigrater [flags]

The two connection flags are the only required inputs. Everything about the v3 side (COOLIFY_SECRET_KEY, the prod.db path, the workload containers) is auto-detected from the running coolify container — you never supply those.

Where do the DSN and key come from?

You don't invent them — coolifygo writes them to its own env file when it is installed, at /data/coolifygo/.env:

DATABASE_URL=postgres://coolifygo:...@coolifygo-postgres:5432/coolifygo
DATA_ENCRYPTION_KEY=<base64 of 32 random bytes>
  • Via install.sh (the one-command path): you do nothing. The wrapper sources /data/coolifygo/.env for you (load_coolifygo_env) and passes both values through — no flags, no exports.

  • Running the binary by hand: source that same file first so the two flags pick the values up from the environment:

    set -a; . /data/coolifygo/.env; set +a   # exports DATABASE_URL + DATA_ENCRYPTION_KEY
    coolfymigrater --yes
    

    or point at it explicitly without exporting anything:

    coolfymigrater \
      --coolifygo-dsn="$(grep '^DATABASE_URL='        /data/coolifygo/.env | cut -d= -f2-)" \
      --coolifygo-key="$(grep '^DATA_ENCRYPTION_KEY=' /data/coolifygo/.env | cut -d= -f2-)"
    

(The file path is configurable in install.sh via COOLIFYGO_ENV.)

Flags
Flag Default Purpose
--coolifygo-dsn $DATABASE_URL Required. coolifygo Postgres DSN to write the migrated rows into.
--coolifygo-key $DATA_ENCRYPTION_KEY Required. coolifygo DATA_ENCRYPTION_KEY — base64 of 32 raw bytes. Secrets are re-encrypted under this key (AES-256-GCM, enc:v1: prefix).
--v3-secret-key auto-detected v3 COOLIFY_SECRET_KEY used to decrypt v3's secret columns. Read from the coolify container's env when blank; pass it explicitly only if that detection fails.
--v3-sqlite auto-extracted Path to v3's prod.db on the host. When blank, it is docker cp'd out of the (frozen) coolify container to a temp dir and cleaned up afterwards.
--phase all all (single-shot), pre-docker (discover → insert, then save state and exit), or post-docker (reload state → takeover → teardown). Used by install.sh to bracket the Docker-engine upgrade.
--state-file /var/lib/coolfymigrater/state.json Where pre-docker writes / post-docker reads the plan JSON.
--dry-run false Print the full plan and exit before any change.
--yes false Skip the interactive confirmation prompts (proceed + teardown).
--no-teardown false Keep the v3 stack alive after the data migration + takeover complete.
Environment variables
Variable Equivalent flag Where it comes from
DATABASE_URL --coolifygo-dsn Written by coolifygo into /data/coolifygo/.env at install.
DATA_ENCRYPTION_KEY --coolifygo-key Written by coolifygo into /data/coolifygo/.env at install.
DOCKER_HOST / DOCKER_* Standard Docker SDK env — points the client at the local daemon socket (defaults to the local socket).
Examples
# 1. Dry run — see exactly what would be migrated, change nothing.
coolfymigrater --coolifygo-dsn=$DATABASE_URL --coolifygo-key=$DATA_ENCRYPTION_KEY --dry-run

# 2. Full single-shot migration, no prompts (env vars already exported).
coolfymigrater --yes

# 3. Migrate the data + take over containers, but LEAVE v3 running
#    (nothing is wiped — useful to validate coolifygo before committing).
coolfymigrater --yes --no-teardown

# 4. Provide v3 secrets explicitly if auto-detection can't reach the container.
coolfymigrater \
  --v3-secret-key=base64:xxxxxxxx... \
  --v3-sqlite=/data/coolify/source/prod.db

# 5. Phase-split run by hand (what install.sh does around a Docker upgrade).
coolfymigrater --phase=pre-docker  --state-file=/tmp/mig.json --yes
#   … upgrade Docker / restart the daemon here …
coolfymigrater --phase=post-docker --state-file=/tmp/mig.json --yes

The connection flags (--coolifygo-dsn / --coolifygo-key) are required in every phase, including post-docker — takeover reopens Postgres to write the real container ids back onto the migrated rows.

install.sh overrides

The wrapper is driven entirely by environment variables, so you can pin versions or point it at forks without editing the script:

MIGRATER_REF=v1.2.3 sudo -E bash install.sh
Variable Default Purpose
GO_VERSION 1.26.5 Minimum Go required (matches go.mod) and the version installed when the host is short of it. A host with this version or newer is used untouched — never downgraded.
GO_INSTALL_DIR /usr/local/go Where the Go tarball is unpacked (removed on exit if the script installed it).
COOLIFYGO_INSTALL_URL gocoolify install.sh Installer used to bring up coolifygo when it isn't already running.
MIGRATER_MODULE github.com/annihilatorrrr/coolifyv32Go Module path passed to go install.
MIGRATER_REF latest Git ref / version to install (@<ref>). go install runs with GOPROXY=direct so it always fetches from GitHub, bypassing the module proxy cache.
STATE_FILE /var/lib/coolfymigrater/state.json Passed through as --state-file to both phases.
COOLIFYGO_ENV /data/coolifygo/.env File sourced for DATABASE_URL + DATA_ENCRYPTION_KEY.
ASSUME_YES 1 When 1, passes --yes to the binary so curl | bash runs unattended. Set to 0 to keep the prompts.

Build & test

go build ./...                    # compile everything
go build -o bin/coolfymigrater .  # build the CLI binary
go vet ./...
go test ./...

Scope

Built for the narrow shape the operator described:

  • v3 manages Dockerfile-built apps only (other build packs error out).
  • v3 manages PostgreSQL + Redis databases only (MySQL / Mongo error out).
  • No persistent storage on application containers.
  • GitHub App git sources only — other git-source auth types are skipped (multiple GitHub App sources are supported).
  • v3 runs on the same host as coolifygo (local Docker socket).

Everything else (services, FQDN/Traefik/SSL, teams, PR previews, remote destinations, Storages) is intentionally skipped.

Phases

  1. discover — inspect the local coolify container for the secret key and container metadata; list every workload on the coolify-infra network.
  2. connect coolifygo postgres — verify DSN + key + that a type=local server row already exists (created by coolifygo's EnsureLocalServer).
  3. freeze — stop coolify and coolify-fluentbit so v3's SQLite is in a consistent state.
  4. extract + readdocker cp /app/db/prod.db out of the already-frozen coolify container (no restart needed — docker cp works on stopped containers and the file is consistent because v3 is quiesced). Opens the copy via modernc.org/sqlite, decrypts every secret column with AES-256-CTR keyed by COOLIFY_SECRET_KEY. Temp file is cleaned up after the read.
  5. plan — map v3 entities onto coolifygo row shapes. Validates that buildPacks and DB types are in scope.
  6. insert — one Postgres transaction writes git_sources → applications → databases. Rollback on any failure leaves coolifygo untouched.
  7. takeover — apps are recreated as coolifygo-<slug>-<id8> on the coolifygo bridge network with the existing image + env + host port binding + coolifygo's coolifygo.managed label. Databases stop, get their data volume copied (not moved) to coolifygo-db-<id8> via a one-shot alpine:3 container, then start fresh as coolifygo-db-<id8> with coolifygo's canonical image (postgres:<v>-alpine / redis:<v>-alpine), env, healthcheck, and network. After each recreate the real new container id + status are written back onto the coolifygo row.
  8. teardown — stop+remove every v3 stack container (coolify, coolify-fluentbit, coolify-proxy, coolify-haproxy); drop the seven v3 infra volumes (coolify-db, coolify-logs, coolify-local-backup, coolify-ssl-certs, two letsencrypt volumes, and the optional coolify-pgdb); reclaim each copied-from database source volume (the originals takeover copied out of — guarded: never touches a coolifygo- volume, force=false so an in-use one is reported, not destroyed); prune coollabsio/coolify, ghcr.io/coollabsio/coolify, and ghcr.io/coollabsio/fluent-bit images plus the migrater's own alpine:3 copy-helper (only if unused); remove the coolify-infra network; and clean host paths (/data/coolify, /var/lib/coolify, /etc/cron.d/coolify-default, /usr/local/bin/coolify). On a completed post-docker run the migrater also removes its own state file and the now-empty state directory.

What gets copied

v3 entity coolifygo target Notes
GithubApp + GitSource git_sources github-app auth_type only. PEM, client secret, webhook secret re-encrypted under coolifygo's AES-256-GCM.
Application + Secret applications env_vars carries decrypted secrets. Branch defaults to main, base_directory to ./, dockerfile_location to Dockerfile. Status set to whatever the live v3 container reports. Host port carried from Docker's actual port bindings (not v3's internal port field, which was for Traefik routing). Apps with no host-published port get port 0 (no host binding).
Database databases Slug regenerated. Volume content copied byte-for-byte. Port + internal_port set to the canonical 5432/6379. Public port carried from Docker's actual host binding (overrides v3 SQLite if they diverge).
running container metadata container_id / image_name on the new row So the boot reconciler doesn't immediately try to recreate something that's already up.

What gets thrown away

  • v3 management containers (coolify, coolify-fluentbit, coolify-proxy, coolify-haproxy) + their infra volumes + the coolify-infra network.
  • The original v3 database source volumes — reclaimed after their contents were copied into the new coolifygo-db-<id8> volumes.
  • v3 host paths (/data/coolify, /var/lib/coolify, the systemd unit hint binary /usr/local/bin/coolify, the /etc/cron.d/coolify-default cron).
  • v3 Traefik / Let's Encrypt state (SSL is out of scope in coolifygo).
  • v3 backup volumes (coolifygo runs its own backup pipeline; old archives are not migrated).
  • coollabsio/* + ghcr.io/coollabsio/* images, and the migrater's own alpine:3 copy-helper (only when no other container still uses it).

Safety

  • Port conflict detection at plan time: the migrater checks for duplicate host ports within the migration batch and against resources already in coolifygo's database. Bails with a descriptive error before any change.
  • Inserts run inside a single Postgres transaction. Rollback on any failure leaves coolifygo in its pre-migration state.
  • Container takeover begins only after the transaction commits. A failed takeover leaves a partial state in coolifygo but never destroys v3's data volumes (those are dropped by the teardown phase, gated by a confirm prompt unless --yes is passed).
  • --dry-run prints the full plan and exits before any change.

Re-runs

  • Post-docker phase can be resumed from the saved state file (/var/lib/coolfymigrater/state.json by default). stopAndRemove tolerates containers that were already cleaned up on a prior attempt, so an interrupted takeover can be picked up where it left off.
  • Pre-docker phase is not idempotent — coolifygo's schema has no unique constraint on names, so re-inserting would create duplicate rows. Drop coolifygo's database (greenfield design) before re-running pre-docker from scratch.
  • A fully successful post-docker run deletes the state file so a stale resume can't confuse a future invocation.

Documentation

Overview

Command coolfymigrater migrates a running Coolify v3 install into coolifygo on the same host. It expects:

  • the local Docker socket is available
  • coolifygo is already provisioned (its postgres + redis are reachable and at least one type=local server row exists)
  • v3 manages only Dockerfile-built apps and postgresql / redis databases
  • apps don't use persistent storage mounts

Operator flow:

coolfymigrater --coolifygo-dsn=... --coolifygo-key=...
  → discover v3 → extract SQLite → plan → confirm → insert → takeover
    → verify → wipe v3

All container ops happen on the local Docker daemon; SSH/remote-engine destinations from v3 are not migrated.

Phase-split execution

Because install.sh upgrades the host's Docker engine between the data import and the container takeover, the binary supports running in two halves so the wrapper can sandwich the daemon restart cleanly:

coolfymigrater --phase=pre-docker  ...   # discover → freeze → extract → insert, then exit
(install.sh upgrades Docker; daemon comes back)
coolfymigrater --phase=post-docker ...   # reload plan → takeover → teardown

State persists between invocations via a JSON file at --state-file (default /var/lib/coolfymigrater/state.json). --phase=all (default) keeps the original single-shot behaviour for operators running without the install.sh wrapper.

Directories

Path Synopsis
internal
cgo
Package cgo writes into the coolifygo Postgres database.
Package cgo writes into the coolifygo Postgres database.
clilog
Package clilog gives the migrater a small, dependency-free way to print phase headers, status lines, and confirmation prompts.
Package clilog gives the migrater a small, dependency-free way to print phase headers, status lines, and confirmation prompts.
discover
Package discover finds a running coolify v3 stack on the local Docker daemon and extracts the bits the migrater needs without the user supplying them:
Package discover finds a running coolify v3 stack on the local Docker daemon and extracts the bits the migrater needs without the user supplying them:
mapper
Package mapper converts decrypted v3 entities into the row shapes coolifygo's tables expect.
Package mapper converts decrypted v3 entities into the row shapes coolifygo's tables expect.
takeover
Package takeover migrates running v3 workloads onto coolifygo's naming, labels, and network.
Package takeover migrates running v3 workloads onto coolifygo's naming, labels, and network.
teardown
Package teardown removes a v3 install from the local host once migration has been verified.
Package teardown removes a v3 install from the local host once migration has been verified.
v3
Package v3 reads a Coolify v3 SQLite database and decrypts its secrets.
Package v3 reads a Coolify v3 SQLite database and decrypts its secrets.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL