Skip to Content

Setup

এই page-এর কাজ একবারই করতে হয়। এরপর আর কখনো এখানে ফিরে আসতে হবে না। শেষে GitHub-এর কাছে আপনার server-এ ঢোকার অনুমতি থাকবে, আর server registry থেকে image টানতে পারবে।

Assumptions

এই page ধরে নিচ্ছে নিচের জিনিসগুলো আপনার server-এ আগে থেকেই আছে:

  • Docker এবং Docker Compose install করা
  • আপনার app হাতে চলছেdocker compose up -d দিলে site খোলে
  • একটি non-root user যা দিয়ে আপনি SSH করেন এবং docker command চালাতে পারেন
  • curl এবং bash আছে

এগুলো কীভাবে করতে হয় তা এই section-এর বিষয় নয়। এখানে শুধু এই চলমান setup-টিকে automatic করা হবে।

শেষ লাইনটি নিয়ে চিন্তার কিছু নেই — Ubuntu 24.04-এ curl আর bash দুটোই আগে থেকেই থাকে। তবু কেন লাগে জেনে রাখুন:

  • curl — deploy-এর শেষে pipeline এটি দিয়ে দেখে site সত্যিই সাড়া দিচ্ছে কিনা
  • bash — deploy script-এ trap ব্যবহার করা হয়, যা fail করলে নিজে থেকে rollback করে। trap ... ERR শুধু bash-এ চলে, সাধারণ sh-এ নয়

Minimal বা Alpine-ভিত্তিক server ব্যবহার করলে দুটোই আগে বসিয়ে নিন।

What CI/CD Needs

মাত্র চারটি জিনিস:

#কীকেন
1দুটি file রাখার একটি folderServer-এ আর code থাকবে না, শুধু compose.yaml আর .env
2শুধু Actions-এর জন্য একটি SSH keyনিজের key বা .pem দেওয়া বিপজ্জনক
3তিনটি GitHub SecretGitHub এগুলো দিয়ে server-এ ঢুকবে
4Registry থেকে image টানার অনুমতিনা থাকলে docker compose pull fail করবে

One Decision First

ধাপে যাওয়ার আগে একটি সিদ্ধান্ত নিতে হবে, কারণ এর উপর নির্ভর করে শেষ ধাপটি আপনার লাগবে কিনা।

আপনার image private রাখবেন, নাকি public করবেন?

GitHub-এ দুটি আলাদা setting আছে। অনেকে ভাবেন একটাই।

Repository visibility → কে আপনার source code দেখতে পাবে Package visibility → কে আপনার image টানতে পারবে

এই দুটি একে অপরের সাথে যুক্ত নয়। Repository private রেখেও package public করা যায়।

Private packagePublic package
Source codeকেউ দেখবে নাকেউ দেখবে না
Image টানাtoken লাগবেযে কেউ পারবে
GHCR সীমা৫০০ MB, মাসে ১ GBসীমাহীন
Server-এ loginলাগবেলাগবে না

Public package-এ কেউ image টেনে আপনার build করা app দেখতে পাবে — compiled file আর runtime node_modules। আপনার .env, source file বা git history যাবে না।

কখন public করা নিরাপদ: আপনার site যদি এমনিতেই সবার জন্য খোলা থাকে — documentation, blog, portfolio — তাহলে image-এ যা আছে তা তো browser-এ সবাই দেখছেই।

কখন নয়: client-এর app, ভেতরে business logic আছে এমন কিছু, বা যেকোনো private product।

একটি জিনিস আগে জেনে নিন — package তৈরিই হয় প্রথম push-এর পরে। এখন GitHub-এ গিয়ে খুঁজলে কিছু পাবেন না। তাই আপনার সিদ্ধান্ত অনুযায়ী পথ দুটি আলাদা।

Private রাখবেন — নিচের শেষ ধাপে token বানিয়ে server-কে login করান। প্রথম deploy-ই কাজ করবে।

Public করবেন — শেষ ধাপটি বাদ দিন। তখন প্রথম deploy-এর deploy job fail করবে, কারণ package তখনো private অবস্থায় তৈরি হয়েছে আর server-এর অনুমতি নেই। Log-এ দেখাবে:

Error response from daemon: error from registry: unauthorized

ভয় পাবেন না, এটাই স্বাভাবিক। এখন package তৈরি হয়ে গেছে, তাই visibility বদলাতে পারবেন:

GitHub → আপনার profile → Packages → package-টি → Package settings → Change visibility → Public

তারপর Actions-এ গিয়ে Re-run failed jobs চাপুন। এবার চলবে।

Restructure the Server Folder

এখন আপনার server-এ সম্ভবত পুরো project clone করা আছে — code, Dockerfile, সব। নতুন পদ্ধতিতে এর কিছুই লাগবে না।

Server-এ শুধু দুটি file থাকবে:

/opt/myapp/ ├── compose.yaml ← image টানে, build করে না └── .env ← secret এবং কোন version চলবে

Server-এ SSH করে folder তৈরি করুন এবং নিজের user-কে মালিকানা দিন:

SERVER — Terminal
sudo mkdir -p /opt/myapp sudo chown -R $USER:$USER /opt/myapp

chown লাইনটি বাদ দেবেন না। Folder-টি root-এর থাকলে GitHub Actions সেখানে .env file লিখতে পারবে না, আর deploy Permission denied দিয়ে fail করবে।

এখন শুধু folder-টি তৈরি রাখুন। ভেতরে কী লিখবেন তা পরের page-এ আছে, কারণ তখন আপনার image-এর নাম জানা থাকবে।

পুরোনো clone করা folder-টি এখনই মুছবেন না। নতুন pipeline কাজ করা শুরু করলে তারপর মুছবেন। ততক্ষণ সেটিই আপনার backup।

Create a Key for GitHub Actions

এখন GitHub-কে server-এ ঢোকার অনুমতি দিতে হবে। এর জন্য একটি নতুন SSH key তৈরি করব — শুধু এই কাজের জন্য।

আপনার ব্যক্তিগত SSH key বা cloud provider-এর দেওয়া .pem file কখনো GitHub-এ দেবেন না। ওই key দিয়ে আপনি নিজে সব server-এ ঢোকেন। একবার ফাঁস হলে সব হারাবেন।

আলাদা key-র সুবিধা: সন্দেহ হলে শুধু authorized_keys থেকে ওই এক লাইন মুছে দিলেই GitHub-এর access বন্ধ, আপনার নিজের কিছুই ভাঙে না।

আপনার নিজের computer-এ key তৈরি করুন:

LOCAL — Terminal
ssh-keygen -t ed25519 -C "github-actions" -f ~/.ssh/gh_actions -N ""
  • -t ed25519 — key-এর ধরন। এখনকার সবচেয়ে ভালো ধরন এটি
  • -C "github-actions" — একটি label, পরে চিনতে সুবিধা হবে
  • -f ~/.ssh/gh_actions — এই নামে save হবে, আপনার আসল key-র উপরে লিখবে না
  • -N "" — কোনো passphrase নেই। Actions একটি machine, সে passphrase টাইপ করতে পারে না

দুটি file তৈরি হলো:

Fileকীকোথায় যাবে
gh_actionsPrivate keyGitHub Secrets-এ
gh_actions.pubPublic keyServer-এ

এবার আপনার computer থেকে public key server-এ পাঠান:

LOCAL — Terminal
cat ~/.ssh/gh_actions.pub | ssh USER@YOUR_SERVER_IP \ "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"

authorized_keys একটি তালিকা। এই file-এ যার public key লেখা আছে, সে server-এ ঢুকতে পারে। আমরা সেখানে GitHub-এর জন্য একটি লাইন যোগ করলাম।

ওই দুটি chmod বাদ দেবেন না।

কী করে

  • chmod 700 ~/.ssh — এই folder-এ শুধু আপনি ঢুকতে পারবেন
  • chmod 600 authorized_keys — এই file শুধু আপনি পড়তে ও লিখতে পারবেন

কেন দরকার

এই file-এ অন্য কেউ লিখতে পারলে সে নিজের key যোগ করে দিতে পারবে। তারপর সে-ও আপনার server-এ ঢুকে যাবে।

SSH কী করে

SSH server এই ঝুঁকিটা জানে। তাই key ব্যবহারের আগে সে permission দেখে নেয়। অন্য কেউ ওখানে লিখতে পারে দেখলে সে key-টি ব্যবহার করে না।

আপনি কী দেখবেন

Permission denied (publickey)

SSH কারণ বলে না। আপনি ভাববেন key ভুল হয়েছে। আসলে key ঠিক আছে, permission ভুল।

Test the Key

তিনটি ছোট পরীক্ষা। তিনটিই আপনার নিজের computer থেকে চালাবেন।

এখানে পাশ করা মানে pipeline-ও পাশ করবে। তাই এই ধাপটি বাদ দেবেন না।

১. Key দিয়ে ঢোকা যায়?

LOCAL — Terminal
ssh -i ~/.ssh/gh_actions USER@YOUR_SERVER_IP

ঢুকতে পারলে ঠিক আছে। exit লিখে বেরিয়ে আসুন।

২. Docker চলে?

LOCAL — Terminal
ssh -i ~/.ssh/gh_actions USER@YOUR_SERVER_IP "docker compose version"

Version দেখালে ঠিক আছে।

৩. Folder-এ লেখা যায়?

LOCAL — Terminal
ssh -i ~/.ssh/gh_actions USER@YOUR_SERVER_IP "touch /opt/myapp/ok && rm /opt/myapp/ok && echo done"

done দেখালে ঠিক আছে।

২ আর ৩ নম্বরে command-টি quote-এর ভেতরে লেখা। এভাবে চালালে server আপনাকে কোনো terminal দেয় না — শুধু ওই command চালিয়ে ফল ফেরত দেয়। GitHub Actions ঠিক এভাবেই কাজ করে।

এই পার্থক্যটা গুরুত্বপূর্ণ। Terminal না খুললে ~/.bashrc file-টি চলে না, আর সেখানে সাজিয়ে রাখা command গুলো পাওয়া যায় না। command not found এলে এটাই কারণ।

তখন server-এ which docker চালিয়ে সম্পূর্ণ path বের করুন, আর workflow-এ সেই path ব্যবহার করুন।

Add Three GitHub Secrets

Secret হলো GitHub-এর ভেতরে লুকিয়ে রাখা তথ্য — password, key, এই ধরনের জিনিস।

দুটি সুরক্ষা পাবেন।

এক — কেউ পড়তে পারে না। একবার save করার পরে আপনি নিজেও আর value-টি দেখতে পাবেন না। শুধু workflow সেটি ব্যবহার করতে পারে।

দুই — log-এ লুকিয়ে যায়। Workflow যদি ভুল করে secret-টি log-এ লিখে ফেলে, GitHub আসল লেখাটি সরিয়ে তারকা চিহ্ন বসিয়ে দেয়:

আপনার secret → ghp_a1b2c3d4e5f6 Log-এ দেখাবে → ***

তাই কেউ Actions-এর log পড়লেও আসল value জানতে পারবে না।

GitHub-এ যান:

Your Repository → Settings → Secrets and variables → Actions → New repository secret

তিনটি secret তৈরি করুন:

NameValue
SERVER_HOSTServer-এর IP address, যেমন 13.234.56.78
SERVER_USERSSH username, যেমন ubuntu
SSH_PRIVATE_KEYgh_actions file-এর সম্পূর্ণ content

আপনার নিজের computer-এ private key-র content দেখতে:

LOCAL — Terminal
cat ~/.ssh/gh_actions

-----BEGIN OPENSSH PRIVATE KEY----- থেকে -----END OPENSSH PRIVATE KEY----- পর্যন্ত সবটুকু copy করুন। শেষ লাইনের পরের খালি লাইনটিও রাখুন। একটি অক্ষর বাদ পড়লে error in libcrypto নামের একটি বিভ্রান্তিকর error আসবে।

Registry-র জন্য কোনো secret লাগবে না। GitHub Actions নিজে থেকেই একটি অস্থায়ী token পায় (GITHUB_TOKEN) যা দিয়ে সে GHCR-এ image পাঠাতে পারে। এটি প্রতিটি run-এর শেষে মুছে যায়।

Registry Access

Package public করবেন ঠিক করে থাকলে এই পুরো ধাপটি বাদ দিন। উপরের callout-এ লেখা পথে যাবেন — প্রথম deploy fail করবে, তারপর package public করে job আবার চালাবেন।

Private package-এর image টানতে server-এর একটি token লাগবে। GitHub-এ গিয়ে বানান:

GitHub → Settings (আপনার profile-এর) → Developer settings → Personal access tokens → Tokens (classic) → Generate new token

শুধু একটি অনুমতি টিক দিন — read:packages। আর কিছু নয়।

write:packages বা repo টিক দেবেন না। Server-এর কাজ শুধু image পড়া। এই token যদি কোনোদিন ফাঁস হয়, তাহলে সে দিয়ে শুধু image পড়া যাবে — কিছু মুছে দেওয়া বা code বদলে দেওয়া যাবে না।

এবার server-কে GHCR-এ login করাতে হবে। Server-এ SSH করে এই command চালান:

SERVER — Terminal
echo "YOUR_TOKEN" | docker login ghcr.io -u YOUR_GITHUB_USERNAME --password-stdin

Login Succeeded দেখালে হয়ে গেল। একবারই করতে হয় — Docker credential মনে রাখে।

এখানে আপনি server-এ login করছেন না — সেখানে তো আগে থেকেই আছেন। এই command server-এর Docker-কে GitHub-এর registry-তে login করায়।

Common Blockers

দুটি সমস্যা খুব সাধারণ। দুটোই চুপচাপ pipeline ভেঙে দেয়, আর error message পড়ে কারণ বোঝা যায় না।

Firewall দিয়ে SSH আটকানো থাকলে

অনেকে নিরাপত্তার জন্য শুধু নিজের বাসার IP থেকে SSH allow করে রাখেন। কিন্তু GitHub runner-এর IP প্রতিবার বদলায়, আর সেটি হাজার হাজার IP-র একটি বিশাল তালিকা। আগে থেকে allow করে রাখার উপায় নেই।

সমাধান হলো IP দিয়ে নয়, key দিয়ে নিরাপত্তা দেওয়া। Port 22 সবার জন্য খোলা রাখুন, কিন্তু password দিয়ে login বন্ধ করে দিন। Server-এ SSH করে চালান:

SERVER — Terminal
printf 'PasswordAuthentication no\nPermitRootLogin no\n' | \ sudo tee /etc/ssh/sshd_config.d/99-hardening.conf sudo sshd -t && sudo systemctl restart ssh

দুটি জিনিস খেয়াল করুন।

Ubuntu-তে সরাসরি /etc/ssh/sshd_config-এ লিখবেন না। ওই file একই setting দুইবার পেলে প্রথমটি মানে, আর cloud provider-এর রাখা file আগে পড়া হয় — তাই আপনার লেখা চুপচাপ অগ্রাহ্য হবে। এজন্যই উপরে sshd_config.d/ folder ব্যবহার করা হয়েছে।

sudo sshd -t অংশটি config-এ ভুল আছে কিনা দেখে নেয়। এটি ছাড়া restart করলে ভুল config-এর কারণে আপনি নিজেই server থেকে বেরিয়ে যেতে পারেন। Restart-এর পরে বর্তমান terminal বন্ধ না করে আরেকটি terminal থেকে SSH করে দেখুন।

fail2ban runner-কে ban করে দিলে

Deploy বারবার fail করলে fail2ban সেই IP ব্যান করতে পারে। তখন পরের deploy টাইমআউট হবে আর error message কিছুই বোঝাবে না। সন্দেহ হলে server-এ গিয়ে দেখুন:

SERVER — Terminal
sudo fail2ban-client status sshd

Real-World Note

আপনার pipeline এখন server-এর একটি চাবি ধরে আছে।

এর মানে GitHub repository-তে যার push করার ক্ষমতা আছে, সে workflow file বদলে server-এ যেকোনো command চালাতে পারে। Repository-র access এখন আর শুধু code-এর access নয় — এটি server-এর access।

চারটি নিয়ম মানুন:

  • root কখনো নয়। SERVER_USER সবসময় সাধারণ user হবে।
  • Branch protection চালু করুন। main-এ সরাসরি push বন্ধ করুন, review ছাড়া merge বন্ধ করুন। Settings → Branches → Add rule। এটি একটিমাত্র সবচেয়ে কার্যকর পদক্ষেপ।
  • Fork থেকে আসা pull request-এ secrets দেবেন না। GitHub এটি default-ই বন্ধ রাখে — চালু করার লোভ সামলান।
  • Key rotate করুন। কেউ team ছাড়লে ~/.ssh/authorized_keys থেকে পুরোনো লাইন মুছে নতুন key বানান।

আরেকটি কথা জেনে রাখুন। docker login করার পরে আপনার token ~/.docker/config.json file-এ জমা থাকে — সেটি encrypt করা নয়, শুধু base64 করা। যে কেউ ওই file পড়তে পারলেই token পেয়ে যাবে। এই কারণেই token-এ শুধু read:packages অনুমতি রাখা এত জরুরি।

একটি দ্রুত পরীক্ষা: আপনার repository-তে যাদের write access আছে তাদের list দেখুন। ওই সবার হাতেই এখন আপনার production server আছে।

Quick Check

পরের page-এ যাওয়ার আগে সব মিলিয়ে নিন:

  • /opt/myapp folder আছে এবং আপনার user-এর মালিকানায়?
  • ssh -i ~/.ssh/gh_actions USER@IP দিয়ে ঢোকা যাচ্ছে?
  • ssh -i ~/.ssh/gh_actions USER@IP "docker compose version" কাজ করছে?
  • /opt/myapp-এ file লেখার test পাশ করেছে?
  • GitHub-এ তিনটি secret যোগ করা হয়েছে?
  • Package private রাখলে — server-এ docker login ghcr.io সফল হয়েছে? (public করলে এটি লাগবে না)

পরবর্তী → First Pipeline

github secrets bangla, ssh key github actions bangla, ghcr login bangla, docker login ghcr, read packages token, non interactive ssh bangla, deploy key setup bangla

Last updated on