Skip to content

Instantly share code, notes, and snippets.

@tabarra
Last active June 2, 2025 04:30
Show Gist options
  • Save tabarra/cb574940cd077dd644e80206e337892c to your computer and use it in GitHub Desktop.
Save tabarra/cb574940cd077dd644e80206e337892c to your computer and use it in GitHub Desktop.
Bun script to Build & Deploy a docker container to Coolify via ECR private repo

Coolify ECR Deploy Script

Setup:

  1. Pre-requirements:
    1. The project needs to be a Docker container
    2. AWS ECR Repository
    3. Coolify Resource set up
    4. Bun needs to be installed on host machine
  2. Need to set up the coolify server to be able to pull your ECR images (detailed below).
  3. On your project's folder, create a .env with your config (template below).
  4. run bun run deploy.ts

For Coolify to be able to get the images, you need to ssh in and do:

snap install aws-cli --classic
nano ~/.aws/credentials # input the config as shown below
# [default]
# aws_access_key_id=KEYKEYKEYKEYKEYKEY
# aws_secret_access_key=TokenTokenTokenTokenTokenTokenTokenToken

crontab -e # input the config as shown below
# 5 */12 * * * /snap/bin/aws ecr get-login-password --region us-east-1 | /usr/bin/docker login --username AWS --password-stdin 999999999999.dkr.ecr.us-east-1.amazonaws.com > /root/cron-docker-login.log

cat /root/.docker/config.json   #after first login, check if this has the creds filled
import { $, sleep, fetch, env } from "bun";
import chalk from "chalk";
//Console helpers
const _cGroup = console.group;
const _cGroupEnd = console.groupEnd;
const groupDiv = '='.repeat(42);
console.group = (...args: any[]) => {
const header = ('== ' + args.join(' ')).padEnd(42, ' ');
console.log(chalk.inverse(groupDiv));
console.log(chalk.inverse(header));
console.log(chalk.inverse(groupDiv));
_cGroup(); //start group
};
console.groupEnd = () => {
process.stdout.write('\n');
_cGroupEnd(); //close group
}
//Configs
const attemptDelay = 5000; // 5 seconds
const attemptLimit = 30; // 30 attempts (5 minutes total)
console.group('Building image');
await $`docker build --progress=plain --pull -t $AWS_ECR_URL/$AWS_ECR_IMAGE:latest .`;
console.groupEnd();
console.group('Getting ECR auth');
await $`aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ECR_URL`;
console.groupEnd();
console.group('Pushing build');
const dockerPushOutput = await $`docker push $AWS_ECR_URL/$AWS_ECR_IMAGE:latest`;
const buildShaMatch = dockerPushOutput.text().match(/latest: digest: (sha256:[a-f0-9]{64})/);
if (!buildShaMatch || buildShaMatch.length < 2) {
console.error('Failed to extract digest from push output:', dockerPushOutput.stdout);
process.exit(1);
}
const buildSha = buildShaMatch[1]!;
console.groupEnd();
//Awaiting the image to be available
console.group('Awaiting image to be available on ECR');
console.log('Checking for image with digest:', buildSha);
let checkPushAttempts = 0;
while (true) {
const awsDesc = await $`aws ecr describe-images --repository-name $AWS_ECR_IMAGE --image-ids imageTag=latest`.quiet();
const output = awsDesc.text();
if (output.includes(buildSha)) {
console.log('Image is available!');
break;
}
console.log('Image not available yet, waiting...');
// Wait before next check
checkPushAttempts++;
if (checkPushAttempts >= attemptLimit) {
console.error('Push check timed out after', attemptLimit * attemptDelay / 1000, 'seconds');
process.exit(1);
}
await sleep(attemptDelay);
};
console.groupEnd();
//Helper function to fetch Coolify API
const fetchCoolifyApi = (path: string, body?: any) => {
const reqUrl = new URL(path, `https://${env.COOLIFY_API_DOMAIN}`);
const options: RequestInit = {
signal: AbortSignal.timeout(2500),
method: body ? 'POST' : 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${env.COOLIFY_API_TOKEN}`,
},
};
if (body) {
options.body = JSON.stringify(body);
}
return fetch(reqUrl.href, options);
}
//Trigger Coolify deploy
console.group('Triggering Coolify deploy');
const deployResp = await fetchCoolifyApi(`/api/v1/deploy?uuid=${env.COOLIFY_RESOURCE_ID}&force=false`);
console.log('Coolify deploy response:', deployResp.status, deployResp.statusText);
const respData = await deployResp.json() as any;
if (!Array.isArray(respData.deployments) || respData.deployments.length === 0) {
console.error('No deployments queed:', respData);
process.exit(1);
}
const deploymentUuid = respData.deployments[0].deployment_uuid;
if (!deploymentUuid) {
console.error('No deployment UUID found:', respData);
process.exit(1);
}
console.groupEnd();
// Await Deployment
let checkDeploymentAttempts = 0;
console.group('Awaiting Coolify deployment:');
while (true) {
const elapsedSecs = Math.floor(checkDeploymentAttempts * attemptDelay / 1000).toString().padStart(2, ' ');
const msgTag = `[${elapsedSecs}s]`;
const statusResp = await fetchCoolifyApi(`/api/v1/deployments/${deploymentUuid}`);
const statusData = await statusResp.json() as any;
if (!statusData || !statusData.status) {
console.error(msgTag, 'Failed to fetch:');
console.log(statusData);
process.exit(1);
}
console.log(msgTag, 'Status:', statusData.status);
if (statusData.status === 'finished') {
break;
} else if (statusData.status === 'failed') {
console.error('Deployment failed:');
console.log(statusData);
process.exit(1);
}
// Wait before next check
checkDeploymentAttempts++;
if (checkDeploymentAttempts >= attemptLimit) {
console.error('Deployment check timed out after', attemptLimit * attemptDelay / 1000, 'seconds');
process.exit(1);
}
await sleep(attemptDelay);
};
console.groupEnd();
console.log(chalk.bgGreenBright.black('== Deployment succeeded! =='));
process.exit(0);
# Used by aws cli
AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXXXX
AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
AWS_DEFAULT_REGION=REGION
# Used by the deploy script directly
AWS_ECR_URL=<ACCOUNT ID>.dkr.ecr.<REGION>.amazonaws.com
AWS_ECR_IMAGE=XXXXXXX/YYYY
COOLIFY_RESOURCE_ID=XXXXXXXXXXXXXXXXXXXX
COOLIFY_API_TOKEN=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
COOLIFY_API_DOMAIN=XXXXXXXXXXXXXX
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment