411411 </ span >
412412 </ a >
413413
414+ </ li >
415+
416+ < li class ="md-nav__item ">
417+ < a href ="#github-actions " class ="md-nav__link ">
418+ < span class ="md-ellipsis ">
419+ ⚡ GitHub Actions
420+ </ span >
421+ </ a >
422+
414423</ li >
415424
416425 </ ul >
521530 </ span >
522531 </ a >
523532
533+ </ li >
534+
535+ < li class ="md-nav__item ">
536+ < a href ="#github-actions " class ="md-nav__link ">
537+ < span class ="md-ellipsis ">
538+ ⚡ GitHub Actions
539+ </ span >
540+ </ a >
541+
524542</ li >
525543
526544 </ ul >
@@ -559,11 +577,10 @@ <h2 id="1-build-your-images">🧱 1. Build Your Images</h2>
559577< div class ="highlight "> < pre > < span > </ span > < code > < span class ="nb "> cd</ span > < span class ="w "> </ span > app
560578docker< span class ="w "> </ span > compose< span class ="w "> </ span > build
561579docker< span class ="w "> </ span > compose< span class ="w "> </ span > push
562- < span class ="nb "> cd</ span > < span class ="w "> </ span > ..
563580</ code > </ pre > </ div >
564581< h2 id ="2-copy-to-server "> 📦 2. Copy to Server</ h2 >
565582< p > Copy your < code > compose.yaml</ code > to the remote host:</ p >
566- < div class ="highlight "> < pre > < span > </ span > < code > scp< span class ="w "> </ span > app/ compose.yaml< span class ="w "> </ span > youruser@yourserver:
583+ < div class ="highlight "> < pre > < span > </ span > < code > scp< span class ="w "> </ span > compose.yaml< span class ="w "> </ span > youruser@yourserver:
567584</ code > </ pre > </ div >
568585< h2 id ="3-set-secrets "> 3. Set Secrets</ h2 >
569586< p > Your app will need credentials such as database passwords or API keys. Choose
@@ -581,7 +598,7 @@ <h2 id="3-launch-the-app">🚀 3. Launch the App</h2>
581598< p > Your backend is now live. 🚀</ p >
582599< hr />
583600< h2 id ="upgrading "> Upgrading</ h2 >
584- < p > To upgrade your app, simply increment the image tag versions in compose.yaml.</ p >
601+ < p > To upgrade your app, simply increment the image tag versions in < code > compose.yaml</ code > .</ p >
585602< p > The rest is the same:</ p >
586603< ol >
587604< li > < code > docker compose build</ code > </ li >
@@ -592,6 +609,64 @@ <h2 id="upgrading">Upgrading</h2>
592609< h2 id ="next-steps "> 🧭 Next Steps</ h2 >
593610< p > If you want zero-downtime deployments, rollback support, or blue-green testing,
594611continue to < a href ="../advanced/ "> Advanced Deployments</ a > .</ p >
612+ < h2 id ="github-actions "> ⚡ GitHub Actions</ h2 >
613+ < p > Here's a Github Actions workflow you can use to automate deployments.</ p >
614+ < div class ="highlight "> < pre > < span > </ span > < code > mkdir< span class ="w "> </ span > -p< span class ="w "> </ span > .github/workflows
615+ </ code > </ pre > </ div >
616+ < details >
617+ < summary > Show full workflow</ summary >
618+
619+ < div class ="highlight "> < span class ="filename "> .github/workflows/ci.yaml</ span > < pre > < span > </ span > < code > < span class ="nt "> name</ span > < span class ="p "> :</ span > < span class ="w "> </ span > < span class ="l l-Scalar l-Scalar-Plain "> Deploy</ span >
620+
621+ < span class ="nt "> on</ span > < span class ="p "> :</ span >
622+ < span class ="w "> </ span > < span class ="nt "> push</ span > < span class ="p "> :</ span >
623+ < span class ="w "> </ span > < span class ="nt "> branches</ span > < span class ="p "> :</ span >
624+ < span class ="w "> </ span > < span class ="p p-Indicator "> -</ span > < span class ="w "> </ span > < span class ="l l-Scalar l-Scalar-Plain "> prod</ span >
625+
626+ < span class ="nt "> jobs</ span > < span class ="p "> :</ span >
627+ < span class ="w "> </ span > < span class ="nt "> deploy</ span > < span class ="p "> :</ span >
628+ < span class ="w "> </ span > < span class ="nt "> runs-on</ span > < span class ="p "> :</ span > < span class ="w "> </ span > < span class ="l l-Scalar l-Scalar-Plain "> ubuntu-latest</ span >
629+ < span class ="w "> </ span > < span class ="nt "> steps</ span > < span class ="p "> :</ span >
630+ < span class ="w "> </ span > < span class ="p p-Indicator "> -</ span > < span class ="w "> </ span > < span class ="nt "> name</ span > < span class ="p "> :</ span > < span class ="w "> </ span > < span class ="l l-Scalar l-Scalar-Plain "> Checkout code</ span >
631+ < span class ="w "> </ span > < span class ="nt "> uses</ span > < span class ="p "> :</ span > < span class ="w "> </ span > < span class ="l l-Scalar l-Scalar-Plain "> actions/checkout@v4</ span >
632+
633+ < span class ="w "> </ span > < span class ="p p-Indicator "> -</ span > < span class ="w "> </ span > < span class ="nt "> name</ span > < span class ="p "> :</ span > < span class ="w "> </ span > < span class ="l l-Scalar l-Scalar-Plain "> Copy compose.yaml from repository to deployment dir</ span >
634+ < span class ="w "> </ span > < span class ="nt "> uses</ span > < span class ="p "> :</ span > < span class ="w "> </ span > < span class ="l l-Scalar l-Scalar-Plain "> appleboy/scp-action@master</ span >
635+ < span class ="w "> </ span > < span class ="nt "> with</ span > < span class ="p "> :</ span >
636+ < span class ="w "> </ span > < span class ="nt "> host</ span > < span class ="p "> :</ span > < span class ="w "> </ span > < span class ="l l-Scalar l-Scalar-Plain "> ${{ secrets.VPS_HOST }}</ span >
637+ < span class ="w "> </ span > < span class ="nt "> username</ span > < span class ="p "> :</ span > < span class ="w "> </ span > < span class ="l l-Scalar l-Scalar-Plain "> ${{ secrets.VPS_USER }}</ span >
638+ < span class ="w "> </ span > < span class ="nt "> key</ span > < span class ="p "> :</ span > < span class ="w "> </ span > < span class ="l l-Scalar l-Scalar-Plain "> ${{ secrets.VPS_SSH_KEY }}</ span >
639+ < span class ="w "> </ span > < span class ="nt "> source</ span > < span class ="p "> :</ span > < span class ="w "> </ span > < span class ="s "> "app/compose.yaml"</ span >
640+ < span class ="w "> </ span > < span class ="nt "> target</ span > < span class ="p "> :</ span > < span class ="w "> </ span > < span class ="s "> "app/"</ span >
641+
642+ < span class ="w "> </ span > < span class ="p p-Indicator "> -</ span > < span class ="w "> </ span > < span class ="nt "> name</ span > < span class ="p "> :</ span > < span class ="w "> </ span > < span class ="l l-Scalar l-Scalar-Plain "> Deploy with Docker Compose</ span >
643+ < span class ="
w "
> </ span > < span class ="
nt "
> uses
</ span > < span class ="
p "
> :
</ span > < span class ="
w "
> </ span > < span class ="
l l-Scalar l-Scalar-Plain "
> appleboy/
[email protected] </ span > 644+ < span class ="w "> </ span > < span class ="nt "> env</ span > < span class ="p "> :</ span >
645+ < span class ="w "> </ span > < span class ="nt "> GHCR_PAT</ span > < span class ="p "> :</ span > < span class ="w "> </ span > < span class ="l l-Scalar l-Scalar-Plain "> ${{ secrets.GHCR_PAT }}</ span >
646+ < span class ="w "> </ span > < span class ="nt "> with</ span > < span class ="p "> :</ span >
647+ < span class ="w "> </ span > < span class ="nt "> host</ span > < span class ="p "> :</ span > < span class ="w "> </ span > < span class ="l l-Scalar l-Scalar-Plain "> ${{ secrets.VPS_HOST }}</ span >
648+ < span class ="w "> </ span > < span class ="nt "> username</ span > < span class ="p "> :</ span > < span class ="w "> </ span > < span class ="l l-Scalar l-Scalar-Plain "> ${{ secrets.VPS_USER }}</ span >
649+ < span class ="w "> </ span > < span class ="nt "> key</ span > < span class ="p "> :</ span > < span class ="w "> </ span > < span class ="l l-Scalar l-Scalar-Plain "> ${{ secrets.VPS_SSH_KEY }}</ span >
650+ < span class ="w "> </ span > < span class ="nt "> envs</ span > < span class ="p "> :</ span > < span class ="w "> </ span > < span class ="l l-Scalar l-Scalar-Plain "> GHCR_PAT</ span >
651+ < span class ="w "> </ span > < span class ="nt "> script</ span > < span class ="p "> :</ span > < span class ="w "> </ span > < span class ="p p-Indicator "> |</ span >
652+ < span class ="w "> </ span > < span class ="no "> set -euo pipefail</ span >
653+ < span class ="w "> </ span > < span class ="no "> cp .env app/</ span >
654+ < span class ="w "> </ span > < span class ="no "> cd app</ span >
655+
656+ < span class ="w "> </ span > < span class ="no "> # Pull images</ span >
657+ < span class ="w "> </ span > < span class ="no "> echo "$GHCR_PAT" | docker login ghcr.io --username "${{ github.actor }}" --password-stdin</ span >
658+ < span class ="w "> </ span > < span class ="no "> DOCKER_CLIENT_TIMEOUT=300 COMPOSE_HTTP_TIMEOUT=300 docker compose pull --quiet</ span >
659+
660+ < span class ="w "> </ span > < span class ="no "> # Bring up stack and run healthchecks</ span >
661+ < span class ="w "> </ span > < span class ="no "> trap 'docker compose down' ERR</ span >
662+ < span class ="w "> </ span > < span class ="no "> docker compose up -d</ span >
663+ < span class ="w "> </ span > < span class ="no "> docker compose exec -T caddy curl -fsS http://caddy:80/healthz</ span >
664+ < span class ="w "> </ span > < span class ="no "> # Add more healthchecks here</ span >
665+ < span class ="w "> </ span > < span class ="no "> # docker compose exec -T caddy curl -fsS http://api:8080/healthz</ span >
666+ < span class ="w "> </ span > < span class ="no "> # docker compose exec -T caddy curl -fsS http://postgrest:3000/</ span >
667+ </ code > </ pre > </ div >
668+
669+ </ details >
595670
596671
597672
0 commit comments