Last active
November 14, 2024 14:25
-
-
Save Geczy/83c1c77389be94ed4709fc283a0d7e23 to your computer and use it in GitHub Desktop.
Migrate Coolify to a new server
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# This script will backup your Coolify instance and move everything to a new server. Docker volumes, Coolify database, and ssh keys | |
# 1. Script must run on the source server | |
# 2. Have all the containers running that you want to migrate | |
# Configuration - Modify as needed | |
sshKeyPath="$HOME/.ssh/your_private_key" # Key to destination server | |
destinationHost="server.example.com" | |
# -- Shouldn't need to modify anything below -- | |
backupSourceDir="/data/coolify/" | |
backupFileName="coolify_backup.tar.gz" | |
# Check if the source directory exists | |
if [ ! -d "$backupSourceDir" ]; then | |
echo "β Source directory $backupSourceDir does not exist" | |
exit 1 | |
fi | |
echo "β Source directory exists" | |
# Check if the SSH key file exists | |
if [ ! -f "$sshKeyPath" ]; then | |
echo "β SSH key file $sshKeyPath does not exist" | |
exit 1 | |
fi | |
echo "β SSH key file exists" | |
# Check if we can SSH to the destination server, ignore "The authenticity of host can't be established." errors | |
if ! ssh -i "$sshKeyPath" -o "StrictHostKeyChecking no" -o "ConnectTimeout=5" root@$destinationHost "exit"; then | |
echo "β SSH connection to $destinationHost failed" | |
exit 1 | |
fi | |
echo "β SSH connection successful" | |
# Get the names of all running Docker containers | |
containerNames=$(docker ps --format '{{.Names}}') | |
# Initialize an empty string to hold the volume paths | |
volumePaths="" | |
# Loop over the container names | |
for containerName in $containerNames; do | |
# Get the volumes for the current container | |
volumeNames=$(docker inspect --format '{{range .Mounts}}{{.Name}}{{end}}' "$containerName") | |
# Loop over the volume names | |
for volumeName in $volumeNames; do | |
# Check if the volume name is not empty | |
if [ -n "$volumeName" ]; then | |
# Add the volume path to the volume paths string | |
volumePaths+=" /var/lib/docker/volumes/$volumeName" | |
fi | |
done | |
done | |
# Calculate the total size of the volumes | |
# shellcheck disable=SC2086 | |
totalSize=$(du -csh $volumePaths 2>/dev/null | grep total | awk '{print $1}') | |
# Print the total size of the volumes | |
echo "β Total size of volumes to migrate: $totalSize" | |
# Print size of backupSourceDir | |
backupSourceDirSize=$(du -csh $backupSourceDir 2>/dev/null | grep total | awk '{print $1}') | |
echo "β Size of the source directory: $backupSourceDirSize" | |
# Check if the backup file already exists | |
if [ ! -f "$backupFileName" ]; then | |
echo "πΈ Backup file does not exist, creating" | |
# Recommend stopping docker before creating the backup | |
echo "πΈ It's recommended to stop all Docker containers before creating the backup | |
Do you want to stop Docker? (y/n)" | |
read -r answer | |
if [ "$answer" != "${answer#[Yy]}" ]; then | |
if ! systemctl stop docker; then | |
echo "β Docker stop failed" | |
exit 1 | |
fi | |
echo "β Docker stopped" | |
else | |
echo "πΈ Docker not stopped, continuing with the backup" | |
fi | |
# shellcheck disable=SC2086 | |
if ! tar --exclude='*.sock' -Pczf $backupFileName -C / $backupSourceDir $HOME/.ssh/authorized_keys $volumePaths; then | |
echo "β Backup file creation failed" | |
exit 1 | |
fi | |
echo "β Backup file created" | |
else | |
echo "πΈ Backup file already exists, skipping creation" | |
fi | |
# Define the remote commands to be executed | |
remoteCommands=" | |
# Check if Docker is a service | |
if systemctl is-active --quiet docker; then | |
# Stop Docker if it's a service | |
if ! systemctl stop docker; then | |
echo 'β Docker stop failed'; | |
exit 1; | |
fi | |
echo 'β Docker stopped'; | |
else | |
echo 'βΉοΈ Docker is not a service, skipping stop command'; | |
fi | |
echo 'πΈ Saving existing authorized keys...'; | |
cp ~/.ssh/authorized_keys ~/.ssh/authorized_keys_backup; | |
echo 'πΈ Extracting backup file...'; | |
if ! tar -Pxzf - -C /; then | |
echo 'β Backup file extraction failed'; | |
exit 1; | |
fi | |
echo 'β Backup file extracted'; | |
echo 'πΈ Merging authorized keys...'; | |
cat ~/.ssh/authorized_keys_backup ~/.ssh/authorized_keys | sort | uniq > ~/.ssh/authorized_keys_temp; | |
mv ~/.ssh/authorized_keys_temp ~/.ssh/authorized_keys; | |
chmod 600 ~/.ssh/authorized_keys; | |
echo 'β Authorized keys merged'; | |
if ! curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash; then | |
echo 'β Coolify installation failed'; | |
exit 1; | |
fi | |
echo 'β Coolify installed'; | |
" | |
# SSH to the destination server, execute the remote commands | |
if ! ssh -i "$sshKeyPath" -o "StrictHostKeyChecking no" root@$destinationHost "$remoteCommands" <$backupFileName; then | |
echo "β Remote commands execution or Docker restart failed" | |
exit 1 | |
fi | |
echo "β Remote commands executed successfully" | |
# Clean up - Ask the user for confirmation before removing the local backup file | |
echo "Do you want to remove the local backup file? (y/n)" | |
read -r answer | |
if [ "$answer" != "${answer#[Yy]}" ]; then | |
if ! rm -f $backupFileName; then | |
echo "β Failed to remove local backup file" | |
exit 1 | |
fi | |
echo "β Local backup file removed" | |
else | |
echo "πΈ Local backup file not removed" | |
fi |
Here is the script modified by o1 to handle a ssh key passphrase using ssh-agent:
#!/bin/bash
# This script will backup your Coolify instance and move everything to a new server. Docker volumes, Coolify database, and ssh keys
# 1. Script must run on the source server
# 2. Have all the containers running that you want to migrate
# Configuration - Modify as needed
sshKeyPath="$HOME/.ssh/your_private_key" # Key to destination server
destinationHost="server.example.com" # destination server IP or domain
# -- Shouldn't need to modify anything below --
backupSourceDir="/data/coolify/"
backupFileName="coolify_backup.tar.gz"
# Function to initialize ssh-agent and add the SSH key
initialize_ssh_agent() {
# Check if ssh-agent is already running
if [ -z "$SSH_AGENT_PID" ] || ! ps -p "$SSH_AGENT_PID" > /dev/null 2>&1; then
echo "π Starting ssh-agent..."
eval "$(ssh-agent -s)"
if [ $? -ne 0 ]; then
echo "β Failed to start ssh-agent"
exit 1
fi
echo "β
ssh-agent started"
else
echo "β
ssh-agent is already running"
fi
# Add the SSH key to the agent
echo "π Adding SSH key to ssh-agent"
ssh-add "$sshKeyPath"
if [ $? -ne 0 ]; then
echo "β Failed to add SSH key. Please ensure the passphrase is correct."
exit 1
fi
echo "β
SSH key added to ssh-agent"
}
# Initialize ssh-agent and add the SSH key
initialize_ssh_agent
# Check if the source directory exists
if [ ! -d "$backupSourceDir" ]; then
echo "β Source directory $backupSourceDir does not exist"
exit 1
fi
echo "β
Source directory exists"
# Check if the SSH key file exists
if [ ! -f "$sshKeyPath" ]; then
echo "β SSH key file $sshKeyPath does not exist"
exit 1
fi
echo "β
SSH key file exists"
# Check if we can SSH to the destination server, ignore "The authenticity of host can't be established." errors
if ! ssh -o "StrictHostKeyChecking no" -o "ConnectTimeout=5" root@"$destinationHost" "exit"; then
echo "β SSH connection to $destinationHost failed"
exit 1
fi
echo "β
SSH connection successful"
# Get the names of all running Docker containers
containerNames=$(docker ps --format '{{.Names}}')
# Initialize an empty string to hold the volume paths
volumePaths=""
# Loop over the container names
for containerName in $containerNames; do
# Get the volumes for the current container
volumeNames=$(docker inspect --format '{{range .Mounts}}{{.Name}}{{end}}' "$containerName")
# Loop over the volume names
for volumeName in $volumeNames; do
# Check if the volume name is not empty
if [ -n "$volumeName" ]; then
# Add the volume path to the volume paths string
volumePaths+=" /var/lib/docker/volumes/$volumeName"
fi
done
done
# Calculate the total size of the volumes
# shellcheck disable=SC2086
totalSize=$(du -csh $volumePaths 2>/dev/null | grep total | awk '{print $1}')
# Print the total size of the volumes
echo "β
Total size of volumes to migrate: $totalSize"
# Print size of backupSourceDir
backupSourceDirSize=$(du -csh "$backupSourceDir" 2>/dev/null | grep total | awk '{print $1}')
echo "β
Size of the source directory: $backupSourceDirSize"
# Check if the backup file already exists
if [ ! -f "$backupFileName" ]; then
echo "πΈ Backup file does not exist, creating"
# Recommend stopping docker before creating the backup
echo "πΈ It's recommended to stop all Docker containers before creating the backup"
read -rp "Do you want to stop Docker? (y/n): " answer
if [[ "$answer" =~ ^[Yy]$ ]]; then
if ! systemctl stop docker; then
echo "β Docker stop failed"
exit 1
fi
echo "β
Docker stopped"
else
echo "πΈ Docker not stopped, continuing with the backup"
fi
# shellcheck disable=SC2086
if ! tar --exclude='*.sock' -Pczf "$backupFileName" -C / "$backupSourceDir" "$HOME/.ssh/authorized_keys" $volumePaths; then
echo "β Backup file creation failed"
exit 1
fi
echo "β
Backup file created"
else
echo "πΈ Backup file already exists, skipping creation"
fi
# Define the remote commands to be executed
remoteCommands="
# Check if Docker is a service
if systemctl is-active --quiet docker; then
# Stop Docker if it's a service
if ! systemctl stop docker; then
echo 'β Docker stop failed';
exit 1;
fi
echo 'β
Docker stopped';
else
echo 'βΉοΈ Docker is not a service, skipping stop command';
fi
echo 'πΈ Saving existing authorized keys...';
cp ~/.ssh/authorized_keys ~/.ssh/authorized_keys_backup;
echo 'πΈ Extracting backup file...';
if ! tar -Pxzf - -C /; then
echo 'β Backup file extraction failed';
exit 1;
fi
echo 'β
Backup file extracted';
echo 'πΈ Merging authorized keys...';
cat ~/.ssh/authorized_keys_backup ~/.ssh/authorized_keys | sort | uniq > ~/.ssh/authorized_keys_temp;
mv ~/.ssh/authorized_keys_temp ~/.ssh/authorized_keys;
chmod 600 ~/.ssh/authorized_keys;
echo 'β
Authorized keys merged';
if ! curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash; then
echo 'β Coolify installation failed';
exit 1;
fi
echo 'β
Coolify installed';
"
# SSH to the destination server, execute the remote commands
if ! ssh root@"$destinationHost" "$remoteCommands" < "$backupFileName"; then
echo "β Remote commands execution or Docker restart failed"
exit 1
fi
echo "β
Remote commands executed successfully"
# Clean up - Ask the user for confirmation before removing the local backup file
echo "Do you want to remove the local backup file? (y/n)"
read -r answer
if [[ "$answer" =~ ^[Yy]$ ]]; then
if ! rm -f "$backupFileName"; then
echo "β Failed to remove local backup file"
exit 1
fi
echo "β
Local backup file removed"
else
echo "πΈ Local backup file not removed"
fi
# Kill ssh-agent if it was started by this script
if [ -n "$SSH_AGENT_PID" ]; then
echo "π Stopping ssh-agent..."
eval "$(ssh-agent -k)"
echo "β
ssh-agent stopped"
fi
Thanks everyone for helping with this.
Thanks a lot , Great work
Thanks very much for everyone's input. Yesterday, I had to migrate a live self-hosted admin and spent the better part of a day trying different approaches. In the end, I found the variation provided by @AlejandroAkbal to be spot-on for my needs, and it worked on the first run.
does this script works for v3? i am using v3 coolify and i want to migrate it to new vps. i guess moving it to v4 isn't possible automatically. so does it work for v3 ?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
glad everyone likes the script <3
@dreadedhamish you can probably ask chatgpt to do that no?