SSD Nodes Learn 🎉 VPS từ $4.99/tháng
Hướng dẫn Matt ConnorBởi Matt Connor

GitHub là gì? Git khác GitHub thế nào trên VPS

Git chạy trên máy để quản lý phiên bản; GitHub là dịch vụ host repository dựa trên Git. Hiểu khác biệt này giúp bạn deploy và lưu lịch sử cấu hình VPS đúng cách.

GitHub là gì?

GitHub là một dịch vụ được host để lưu trữ các Git repository và xây dựng một website xung quanh chúng. Git là chương trình quản lý phiên bản chạy trên máy tính hoặc server của bạn. GitHub là sản phẩm của một công ty được xây dựng trên Git, thuộc Microsoft từ năm 2018. Bạn có thể dùng Git hằng ngày mà không bao giờ mở GitHub. Bạn không thể dùng GitHub nếu không có Git.

Điều này trở nên quan trọng ngay khi bạn sở hữu một VPS (virtual private server). Git ghi lại lịch sử của các file cấu hình và deploy script. GitHub là nơi lưu một bản sao của lịch sử đó khi server không lưu bản sao, đồng thời cung cấp nơi chạy build và review. Hướng dẫn này theo dõi một ví dụ từ một thư mục trống đến khi deploy lên server, đồng thời giải thích từng thuật ngữ mới ngay lần đầu bạn gặp nó.

Git tự làm gì

Git là một hệ thống quản lý phiên bản: nó ghi lại trạng thái của một thư mục theo thời gian, để bạn biết nội dung nào đã thay đổi, thay đổi khi nào và vì sao. Git được viết vào năm 2005 để phục vụ việc phát triển Linux kernel. Git là hệ thống phân tán, nghĩa là mỗi bản sao của repository đều chứa toàn bộ lịch sử. Thiết kế của Git không có server trung tâm. Laptop của đồng nghiệp cũng là một bản sao đầy đủ như bất kỳ server nào.

Hãy cài đặt Git và thiết lập danh tính của bạn. Git sẽ không ghi nhận một commit nếu thiếu tên và địa chỉ email, vì cả hai thông tin này được ghi trực tiếp vào commit.

sudo apt update && sudo apt install -y git
git --version
git config --global user.name "Your Name"
git config --global user.email "you@example.com"

Trên Ubuntu 24.04, git --version in ra git version 2.43.0. Mọi release trong vài năm gần đây đều hoạt động theo cách tương tự đối với toàn bộ nội dung bên dưới.

Ví dụ: một repository chứa các file deploy cho VPS

Repository, thường được gọi ngắn gọn là “repo”, là một thư mục được Git theo dõi. Thư mục này trở thành repository khi bạn chạy git init, lệnh này tạo một thư mục ẩn .git bên trong. Thư mục đó chính là repository. Xóa .git thì bạn chỉ còn một thư mục thông thường không có lịch sử.

mkdir vps-deploy && cd vps-deploy
git init -b main
printf '.env\n*.key\n' > .gitignore

-b main đặt tên cho branch đầu tiên là main. Nếu bỏ tùy chọn này, Git sẽ in một đoạn hướng dẫn dài về tên branch mặc định. .gitignore liệt kê các đường dẫn mà Git tuyệt đối không được theo dõi. Hãy ghi file chứa secret vào đó ngay từ ngày đầu, vì một file đã từng được commit sẽ vẫn nằm trong lịch sử sau khi bạn xóa file. Muốn xóa đúng cách, bạn phải viết lại mọi commit được tạo sau đó.

Commit: đơn vị của lịch sử

Bây giờ hãy thêm một script và ghi lại thay đổi đó.

printf '#!/bin/sh\nsudo systemctl restart caddy\n' > restart.sh
git add restart.sh .gitignore
git commit -m "Add restart script and gitignore"
git log --oneline

git add đưa một thay đổi vào staging area, tức danh sách những gì sẽ được đưa vào commit tiếp theo. git commit ghi danh sách đó vào lịch sử dưới dạng một mục. Một commit chứa snapshot của mọi file được theo dõi, message, tác giả, timestamp và con trỏ đến commit trước đó. git log --oneline in mỗi commit trên một dòng; mỗi dòng bắt đầu bằng một hash ngắn như a1b2c3d. Hash đó là tên của commit, và gần như mọi lệnh Git đều chấp nhận tên này.

Nếu bỏ qua bước git add, git commit sẽ trả về no changes added to commit (use "git add" and/or "git commit -a"). Không có gì bị hỏng. Git đang cho biết staging area trống, nên không có gì để tạo snapshot. git status là lệnh cần chạy mỗi khi bạn không biết phải làm gì: lệnh này cho biết branch hiện tại, các thay đổi đã stage và những file Git nhìn thấy nhưng chưa theo dõi.

Nhánh: một dòng lịch sử thứ hai

Một branch là một con trỏ di động trỏ đến một commit. main là một branch và không có gì đặc biệt đối với Git. Tạo branch không tốn chi phí vì Git ghi một con trỏ mới thay vì sao chép các file của bạn.

git switch -c add-backup
printf '#!/bin/sh\nrestic backup /srv\n' > backup.sh
git add backup.sh
git commit -m "Add nightly backup"
git switch main
ls

Sau git switch main, backup.sh không còn xuất hiện trong danh sách. Không có gì bị xóa. File này tồn tại trên branch add-backup, còn main chưa từng có file đó, nên Git đã xóa file khỏi working directory khi bạn chuyển branch. Điều này thường khiến mọi người bất ngờ một lần. git switch add-backup sẽ đưa file trở lại.

Remote: nơi GitHub cuối cùng xuất hiện

Từ đầu đến giờ, mọi thao tác đều chạy trên một máy và hoàn toàn không dùng mạng. Remote là một URL có tên trỏ đến một bản sao khác của cùng repository. GitHub lưu trữ một bản sao như vậy cho bạn. Tên quy ước của remote chính là origin.

Tạo một repository rỗng trên website GitHub, rồi kết nối đến repository đó. Ở đây nên dùng SSH thay vì HTTPS: SSH key là một file do bạn kiểm soát và không hết hạn như personal access token.

ssh-keygen -t ed25519 -C "vps-deploy"
cat ~/.ssh/id_ed25519.pub
ssh -T git@github.com

Dán public key được in ra vào trang SSH keys trong tài khoản GitHub, rồi chạy lại lệnh kiểm tra. Một key hoạt động sẽ trả lời Hi yourname! You've successfully authenticated, but GitHub does not provide shell access. GitHub không cấp shell cho bạn, nên lời từ chối này là trường hợp thành công. git@github.com: Permission denied (publickey). nghĩa là key của bạn chưa được gửi lên hoặc không được chấp nhận, vì vậy hãy kiểm tra xem bạn đã dán file .pub chứ không phải private key nằm cạnh nó.

git remote add origin git@github.com:yourname/vps-deploy.git
git push -u origin main

git push gửi các commit của bạn lên remote. -u ghi rằng main local theo dõi main remote, nên về sau chỉ cần git push là đủ. git clone <url> là thao tác ngược lại trên một máy mới: nó sao chép toàn bộ repository cùng lịch sử và tự thiết lập origin cho bạn. Remote HTTPS cũng hoạt động, và nó truyền qua cùng protocol như mọi trang web, nên hữu ích trên những network chặn outbound port 22. Nếu câu này cần được giải thích thêm, HTTP request thực sự gồm những gì trình bày cơ chế đó.

Pull request, issue và fork: các phần thuộc về GitHub, không phải Git

Mọi nội dung ở trên đều là Git và hoạt động với mọi server. Ba thuật ngữ dưới đây là các tính năng của GitHub. Các nền tảng khác sao chép chúng, còn bản thân Git không biết gì về chúng.

Pull request (PR) là yêu cầu merge một branch vào branch khác, kèm một trang để thảo luận. Bạn push add-backup, mở một PR vào main, rồi trang web hiển thị khác biệt theo từng commit. Mọi người có thể bình luận trên từng dòng. Các kiểm tra tự động báo pass hoặc fail đối với branch đó. Khi bạn bấm merge, GitHub tự thực hiện việc merge trên bản sao của họ, rồi cập nhật main. Tên gọi này xuất phát từ quy trình ban đầu, trong đó bạn yêu cầu maintainer pull branch của bạn vào branch của họ.

Issue là một thread được đánh số cho một bug hoặc một task. Issue nằm trong database của GitHub, không nằm trong repository của bạn. Bạn cần biết điều này trước khi chọn host: clone repo sẽ có toàn bộ commit, nhưng không có issue nào. Muốn lấy issue ra, bạn phải gọi API.

Fork là bản sao repository của người khác trên server, thuộc về tài khoản của bạn. Bạn có quyền ghi trên bản sao đó, push branch vào đó, rồi mở pull request từ bản sao của mình về repository gốc. Đây là cách bạn đóng góp cho một project mà maintainer chưa từng biết bạn. Fork là một clone nằm trên GitHub và lưu thông tin về repository gốc.

Software đọc cả ba loại này thông qua cùng API mà người dùng cũng sử dụng. Một agent review pull request chạy trên server của bạn theo dõi PR mới, đọc diff và đăng bình luận trên từng dòng. Các quy ước như file AGENTS.md ở root của repository tồn tại vì repo hiện được tools cũng như người dùng đọc.

GitHub thực sự làm gì cho chủ VPS

Bắt đầu với việc lưu trữ ngoài máy chủ. Các deploy script và playbook của bạn phải được lưu ở nơi không phải chính máy chủ mà chúng cấu hình. Khi cần, hãy dựng lại VPS từ image mới, clone rồi chạy. Giữ repository đó ở chế độ private và cấp cho máy chủ một deploy key: một SSH key được đăng ký cho một repository duy nhất thay vì toàn bộ account của bạn, đồng thời đặt ở chế độ chỉ đọc. Nếu deploy key chỉ đọc bị lộ, chỉ một repo bị ảnh hưởng. Nếu account key bị lộ, mọi thứ bạn có thể push đều bị ảnh hưởng.

sudo git clone git@github.com:yourname/vps-deploy.git /srv/vps-deploy
cd /srv/vps-deploy
git pull --ff-only

--ff-only từ chối tạo merge commit. Trên máy chủ chỉ nhận thay đổi, merge luôn là một lỗi ngoài ý muốn, vì vậy flag này biến lịch sử khó hiểu thành lỗi rõ ràng fatal: Not possible to fast-forward, aborting. Có thay đổi nào đó trên máy chủ nhưng lẽ ra không được có. Hãy tìm nguyên nhân trước khi pull lần nữa.

Nếu clone bằng root rồi chạy Git bằng user khác, bạn sẽ gặp fatal: detected dubious ownership in repository at '/srv/vps-deploy'. Git từ chối đọc repository thuộc sở hữu của user khác, vì một .git/config độc hại có thể khiến Git chạy các command. Sửa quyền sở hữu bằng chown thay vì thêm một ngoại lệ safe.directory, vì ngoại lệ chỉ tắt kiểm tra mà không loại bỏ nguyên nhân.

GitHub Actions: pipeline build và deploy

Actions là hệ thống CI/CD (tích hợp liên tục và phân phối liên tục) của GitHub. Commit một file YAML vào .github/workflows/, GitHub sẽ chạy file đó khi sự kiện bạn chỉ định xảy ra.

name: check
on:
  push:
    branches: [main]
jobs:
  shellcheck:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - run: sudo apt-get update && sudo apt-get install -y shellcheck
      - run: shellcheck *.sh

File này là một workflow. Một job chạy trên một máy. Một step là một lệnh hoặc một action đã được publish. uses: tải một action từ repository khác, còn @v7 ghim major version của action đó (v7 là phiên bản hiện tại của actions/checkout tính đến tháng 8 năm 2026). Luôn ghim phiên bản, vì action không được ghim có thể chạy code mà bạn chưa đọc và code đó có quyền truy cập secrets của bạn.

runs-on: ubuntu-latest yêu cầu GitHub cấp một máy ảo mới và máy này bị hủy khi job kết thúc. Standard runner miễn phí trên các repository public. Tính đến tháng 8 năm 2026, free plan bao gồm 2,000 phút mỗi tháng cho repository private. Hãy kiểm tra trang pricing hiện tại trước khi lập ngân sách dựa trên con số đó.

Secrets được lưu trong repository settings và được đọc dưới dạng ${{ secrets.DEPLOY_KEY }}. Workflow được kích hoạt bởi pull request từ fork sẽ nhận token chỉ có quyền đọc và không được truy cập các secrets đó. Nếu không, người lạ có thể mở một PR chỉ để in các secrets này ra.

Chạy Actions runner trên VPS của bạn

runs-on: self-hosted gửi job đến một máy do bạn sở hữu. Trang cài đặt runner của repository cung cấp một dòng tải xuống, địa chỉ web của repository và một registration token có hiệu lực trong một giờ. Đưa hai giá trị sau vào REPO_URLRUNNER_TOKEN, rồi quá trình thiết lập chỉ cần 3 lệnh.

./config.sh --url "$REPO_URL" --token "$RUNNER_TOKEN"
sudo ./svc.sh install
sudo ./svc.sh start
./svc.sh status

svc.sh status phải hiển thị service đang active và các dòng log gần đây. Runner mở kết nối HTTPS đi ra đến GitHub để nhận job, vì vậy bạn không cần mở cổng inbound cho nó. svc.sh install ghi unit systemd, và đây là bước nhiều người bỏ qua: nếu không có bước này, runner sẽ thoát cùng phiên SSH của bạn và mọi job sau đó sẽ nằm ở trạng thái queued mà không có thông báo lỗi. hướng dẫn đầy đủ về cách thiết lập self-hosted runner trên VPS trình bày cách hardening và cleanup cần thiết cho một runner chạy lâu dài.

Lợi ích là deploy không còn cần một SSH key inbound có thể truy cập từ Internet, vì job đã chạy ngay trên máy đó. Build cache cũng được giữ nóng giữa các lần chạy, và không có bộ đếm phút nào tính phí.

Có một cảnh báo bắt buộc phải lưu ý. Tài liệu chính thức của GitHub khuyến nghị chỉ dùng self-hosted runner cho repository private, vì fork của repository public có thể chạy code nguy hiểm trên runner của bạn bằng cách mở pull request. Runner thực thi mọi nội dung mà file workflow trên branch đó chỉ định. Với một repo private nơi bạn kiểm soát người có quyền push, rủi ro khá thấp. Với một repo public, hãy xem self-hosted runner là một máy mà người lạ có thể thực thi code trên đó.

Bạn có thực sự cần GitHub không?

Không. Git là tiêu chuẩn, còn GitHub là một dịch vụ tiện lợi. Forgejo và Gitea là các forge tự host; forge là Git host có tích hợp issue và pull request. Cả hai được phát hành dưới dạng một Go binary duy nhất và đều chạy được trên VPS nhỏ. Forgejo là fork của Gitea từ năm 2022 và hiện vận hành Codeberg. Di chuyển một repository chỉ cần một command vì wire protocol giống hệt nhau.

git remote -v
git remote set-url origin git@git.example.com:you/vps-deploy.git
git push origin main

Mọi commit đều được chuyển sang vì mỗi bản clone đã chứa toàn bộ lịch sử. Phần không được chuyển là lớp GitHub xây dựng bên trên: issue và các thread của pull request. CI cũng không được chuyển. Forgejo có implementation Actions riêng, đọc YAML tương tự từ .forgejo/workflows/. Tài liệu của Forgejo nói rõ các giới hạn: GitHub Actions và Forgejo Actions không giống nhau, nên một số thứ có thể không chạy ngay. Forgejo cũng cần runner riêng. Hãy lập kế hoạch cho bước này như một lần port, không phải một lần copy.

Lý do thực tế khiến hầu hết dự án vẫn ở lại là contributor. Code public phải nằm ở nơi mọi người đã có account. Các deploy script riêng của bạn thì không cần như vậy. Đây là hai quyết định riêng biệt, và bạn có thể đưa ra câu trả lời khác nhau cho từng quyết định.

Lỗi nào xảy ra trước và thông báo lỗi có ý nghĩa gì

Push bị từ chối. Bạn thấy:

 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'github.com:yourname/vps-deploy.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally.

Có thay đổi đã được push từ sau lần pull gần nhất của bạn, thường là chỉnh sửa bạn thực hiện trong web editor. Chạy git pull --rebase để replay các commit của bạn lên trên các commit đó, rồi push lại. Tránh dùng git push --force trên branch dùng chung, vì lệnh này xóa các commit khác khỏi branch đó trên server.

fatal: refusing to merge unrelated histories. Bạn đã chạy git init cục bộ để GitHub tạo repository kèm README. Hai lịch sử không có commit chung, nên Git không thể tự suy đoán. Cách xử lý sạch nhất là clone bản sao trên GitHub vào một thư mục mới rồi chuyển các file của bạn vào đó.

error: src refspec main does not match any. Branch bạn chỉ định không tồn tại ở đây. Thông thường repository chưa có commit nào, hoặc branch của bạn có tên là master. git branch --show-current sẽ xác định nguyên nhân.

Một secret đã xuất hiện trong commit. Hãy rotate credential ngay. Coi credential đó là public từ thời điểm nó được push, vì fork, mirror và các bản cache có thể vẫn giữ những bản sao mà bạn không thể xóa.

FAQ

GitHub có phải là Git không?

Không. Git là một chương trình quản lý phiên bản được cài trên máy và hoạt động không cần network hay account. GitHub là một dịch vụ thương mại được host, dùng để lưu Git repository và bổ sung web interface, issue, pull request và CI. Git được phát hành vào năm 2005, còn GitHub ra mắt vào năm 2008 dựa trên Git. Bạn có thể dùng Git mãi mà không cần GitHub. Mọi tính năng của GitHub đều phụ thuộc vào Git bên dưới.

Tôi có cần account GitHub để dùng Git trên VPS không?

Không. git init, git commitgit log hoạt động trên server hoàn toàn không cấu hình remote nào. Như vậy đã đủ để theo dõi thay đổi của các file /etc hoặc deploy script. Account trở nên hữu ích khi bạn muốn có một bản sao của history vẫn còn sau khi server gặp sự cố, hoặc muốn một máy thứ hai có thể clone repository. Các forge tự host như Forgejo và Gitea đáp ứng cùng nhu cầu trên phần cứng do bạn sở hữu. Một SSH remote thông thường trỏ đến bare repository trên máy khác cũng hoạt động mà không cần forge software.

Pull request là gì?

Pull request là yêu cầu merge một branch vào branch khác, kèm theo một trang thảo luận. Bạn push một branch, mở PR vào main, rồi host hiển thị các thay đổi theo từng commit để reviewer có thể bình luận trên từng dòng và các kiểm tra tự động báo pass hoặc fail. Đây là tính năng của GitHub, không phải của Git, nên bản thân Git không có command cho tính năng này. Các host khác cũng triển khai ý tưởng tương tự và đôi khi gọi là merge request.

Tôi có nên chạy GitHub Actions runner trên VPS của mình không?

Với repository riêng tư, thường là có. Job chạy trên phần cứng bạn đã trả phí, không bị tính theo số phút, build cache luôn sẵn, và deploy không còn cần expose SSH key inbound ra Internet. Runner sẽ kết nối outbound đến GitHub để nhận job. Với repository công khai, GitHub khuyến cáo không nên làm vậy: bất kỳ ai cũng có thể fork repo của bạn và mở pull request có workflow chạy code trên máy của bạn.

Sau này tôi có thể chuyển repository khỏi GitHub không?

Code thì có, và khá dễ. Mỗi clone chứa toàn bộ history, nên git remote set-url origin <new url> rồi push sẽ chuyển mọi thứ có trong một commit. Phần còn lại là lớp do GitHub quản lý: issue, thảo luận trong pull request và history của Actions nằm trong database của GitHub, không nằm trong thư mục .git của bạn. Công cụ migration có thể sao chép issue thông qua API, còn các workflow file thường phải chỉnh sửa để phù hợp với CI của host mới. Đây là lý do nên đặt tài liệu chính thức trong repository thay vì trong các issue thread.

#github#git#version-control#ci-cd#developer-tools