AI & AUTOMATIONSELF HOSTING

Hướng dẫn triển khai Forem trên macOS với OrbStack

Đợt Back to School của Apple vừa rồi mình thấy con Mac Mini M4 giá tốt quá nên mua trải nghiệm, dùng chán rồi nên giờ mang ra làm cái homelab để cài mấy ứng dụng nhỏ nhỏ. Tiện cài thử Forem làm cái cộng đồng chia sẻ về AI và Automation, nên làm lại bài hướng dẫn cho anh em nào cũng có cùng nhu cầu.

Tuy nhiên, việc triển khai Forem (mã nguồn của DEV.to) trên macOS sử dụng chip Apple Silicon (M1/M2/M3/M4) thực sự là một “kiếp nạn”. Nếu bạn cố chạy Docker trực tiếp trên Mac, bạn sẽ đụng phải hàng tá vấn đề: từ lỗi biên dịch thư viện nokogiri, xung đột kiến trúc amd64 vs arm64, cho đến cơn ác mộng về quyền ghi file (permission denied).

Sau nhiều lần đập đi xây lại, mình đã tìm ra giải pháp tối ưu nhất cho anh em dùng Mac: Chạy một máy ảo Linux (Ubuntu) nhẹ thông qua OrbStack, và triển khai Forem bên trong đó. Bài viết này mình sẽ hướng dẫn chi tiết từng bước, bao gồm cả cách “vá” những lỗi mà tài liệu chính thức của Forem chưa cập nhật kịp.

Nào, pha cốc cà phê và bắt đầu nhé!

AI & Automation (vnROM)

Chuẩn bị môi trường (OrbStack & Ubuntu VM)

Thay vì dùng Docker Desktop, chúng ta dùng OrbStack vì nó nhẹ, nhanh và hỗ trợ quản lý máy ảo Linux cực tốt. Nếu chưa cài OrbStack thì bạn xem bài hướng dẫn này: OrbStack – Lựa chọn thay thế lý tưởng cho Docker Desktop trên macOS

1. Tạo máy ảo Ubuntu

Mở Terminal trên Mac và chạy lệnh sau để tạo một máy ảo Ubuntu 24.04 (Noble). Chúng ta sẽ đặt tên cho máy ảo này là forem-server:

orb create ubuntu:noble forem-server

2. Truy cập vào máy ảo

Từ bước này trở đi, mọi câu lệnh chúng ta sẽ gõ bên trong máy ảo này, không phải trên Terminal gốc của Mac nhé. Để chui vào máy ảo, bạn gõ:

orb -m forem-server

3. Cài đặt Docker & Docker Compose bên trong VM

Mặc dù OrbStack đã tích hợp Docker, nhưng để có một môi trường khép kín chuẩn Production (giống như bạn đang thuê VPS thật), mình khuyên nên cài đặt Docker Engine riêng cho Ubuntu bên trong máy ảo này.

Chạy lần lượt các lệnh sau để cài đặt:

# Cập nhật hệ thống
sudo apt-get update && sudo apt-get upgrade -y

# Cài đặt Docker
sudo apt-get install -y docker.io docker-compose-v2

# Thêm user hiện tại vào nhóm docker (để không cần gõ sudo)
sudo usermod -aG docker $USER
newgrp docker

Tải mã nguồn & cấu hình

1. Clone Code (Lưu ý quan trọng)

Tuyệt đối không clone code ở ngoài Mac rồi mount vào. Hãy clone trực tiếp vào ổ cứng của máy ảo để đạt tốc độ tối đa và tránh lỗi Permission denied.

cd ~
git clone https://github.com/forem/forem.git
cd forem

2. “Vá” lỗi Containerfile

Image gốc của Forem hiện tại đang dính 2 lỗi rất nặng khiến bạn không thể build được:

  1. Repository của Node.js (NodeSource) bị cũ, gây lỗi 404 khi apt update.
  2. Thiếu đường dẫn Bash chuẩn, gây lỗi exec: "/usr/bin/bash": not found.

Thay vì mở file ra sửa thủ công dễ nhầm, bạn chạy 2 lệnh sed này để tự động vá lỗi:

# Fix 1: Xóa repo NodeSource cũ gây lỗi build 404
sed -i 's/apt update/rm -f \/etc\/apt\/sources.list.d\/nodesource.list \&\& apt update/g' Containerfile

# Fix 2: Tạo symlink cho bash (Sửa lỗi: exec: "/usr/bin/bash": not found)
sed -i '/apt-get install -yq --no-install-recommends \\/a \      ln -s /bin/bash /usr/bin/bash && \\' Containerfile

3. Cấu hình file .env

Tạo file biến môi trường .env:

nano .env

Dưới đây là nội dung file cấu hình đã được tối ưu. Bạn hãy copy và dán vào, nhớ thay đổi các thông tin ten-mien-cua-ban cho phù hợp:

# APP IDENTITY
APP_DOMAIN="ten-mien-cua-ban.com" 
APP_PROTOCOL="https://"
COMMUNITY_NAME="My Community"
DEFAULT_EMAIL="[email protected]" # Phải trùng với SMTP User bên dưới
TZ=Asia/Ho_Chi_Minh

# SECRETS (Bạn có thể tạo chuỗi tại đây: https://tools.vnrom.me/token-generator)
FOREM_OWNER_SECRET="chuoi_bi_mat_dai_ngoang_1"
SECRET_KEY_BASE="chuoi_bi_mat_dai_ngoang_2"

# DATABASE & REDIS (Trỏ về service name, KHÔNG dùng localhost)
DATABASE_URL="postgresql://forem:forem@postgres:5432/forem_production"
DATABASE_NAME="forem_production"
DATABASE_POOL_SIZE=5
REDIS_URL="redis://redis:6379"
REDIS_SESSIONS_URL="redis://redis:6379"
REDIS_SIDEKIQ_URL="redis://redis:6379"

# --- ENVIRONMENT ---
RAILS_ENV="production"
NODE_ENV="production"
RAILS_SERVE_STATIC_FILES="true"
RAILS_LOG_TO_STDOUT="true"

# --- SMTP (Ví dụ dùng Larksuite/Gmail) ---
SMTP_ADDRESS="smtp.larksuite.com"
SMTP_PORT="465"
SMTP_DOMAIN="ten-mien-cua-ban.com"
SMTP_USER_NAME="[email protected]"
SMTP_PASSWORD="mat_khau_ung_dung"
SMTP_AUTHENTICATION="login"
SMTP_TLS=true
SMTP_ENABLE_STARTTLS_AUTO=false
SMTP_OPENSSL_VERIFY_MODE="peer"

# --- SECURITY ---
FORCE_SSL_IN_RAILS="true"

4. Tạo file docker-compose.yml

File mặc định của Forem khá rối rắm. Chúng ta sẽ xóa file cũ và tạo một file mới gọn gàng hơn, tối ưu cho chạy production.

nano docker-compose.yml

Copy và paste nội dung sau (Lưu ý: dòng target: production là chìa khóa để fix lỗi thiếu Gemfile khi build):

services:
  postgres:
    image: postgres:13
    container_name: forem_postgres
    restart: always
    env_file:
      - .env
    environment:
      POSTGRES_USER: forem
      POSTGRES_PASSWORD: forem
      POSTGRES_DB: forem_production
    volumes:
      - db_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "forem"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:6.2-alpine
    container_name: forem_redis
    restart: always
    env_file:
      - .env
    volumes:
      - redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

  web:
    build:
      context: .
      dockerfile: Containerfile
      target: production  # BẮT BUỘC CÓ để copy code vào image
    container_name: forem_web
    restart: always
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    env_file:
      - .env
    ports:
      - "3000:3000"
    command: bundle exec rails s -b 0.0.0.0
    volumes:
      - uploads_data:/opt/apps/forem/public/uploads
      - assets_data:/opt/apps/forem/public/assets

  sidekiq:
    build:
      context: .
      dockerfile: Containerfile
      target: production
    container_name: forem_sidekiq
    restart: always
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    env_file:
      - .env
    command: bundle exec sidekiq

volumes:
  db_data:
  redis_data:
  uploads_data:
  assets_data:

Build và chạy

Đây là lúc chúng ta kiểm chứng thành quả. Hãy chạy lần lượt các lệnh sau thật cẩn thận:

1. Build Image:

Quá trình này sẽ hơi lâu một chút tùy vào mạng và cấu hình máy ảo.

docker compose build

2. Khởi tạo Database:

Chúng ta dùng bundle exec thay vì bin/rails để đảm bảo lệnh được chạy trong đúng ngữ cảnh của Ruby gems.

docker compose run --rm web bundle exec rails db:setup

3. Fix quyền thư mục Upload (Bắt buộc):

Nếu bỏ qua bước này, Forem sẽ chạy được nhưng bạn không thể upload avatar hay ảnh bài viết (Lỗi Errno::EACCES). Docker thường gán quyền root cho volume mới tạo, trong khi user chạy app lại là forem (UID 1000).

docker compose run --rm -u root web mkdir -p /opt/apps/forem/public/uploads/tmp
docker compose run --rm -u root web chown -R 1000:1000 /opt/apps/forem/public
docker compose run --rm -u root web chmod -R 777 /opt/apps/forem/public

4. Biên dịch giao diện:

docker compose run --rm web bundle exec rails assets:precompile

5. Khởi động hệ thống:

docker compose up -d

Tạo tài khoản Admin

Rất nhiều bạn sau khi cài xong, hí hửng vào web đăng ký tài khoản thì bị lỗi. Nguyên nhân thường là do SMTP chưa thông, email xác nhận không gửi đi được.

Cách “chắc cú” nhất là tạo tài khoản Super Admin trực tiếp qua dòng lệnh console:

  1. Truy cập Console:
docker compose exec web bundle exec rails c
  1. Gõ các lệnh sau để tạo Super Admin:
# Tạo user
u = User.new(
  name: "Super Admin", 
  username: "admin", 
  email: "[email protected]", 
  password: "MatKhauSieuManh123", 
  password_confirmation: "MatKhauSieuManh123"
)
u.save!

# Kích hoạt & Cấp quyền
u.confirm
u.add_role(:admin)
u.add_role(:super_admin)
u.save!
exit

Bây giờ bạn có thể đăng nhập ngay tại https://ten-mien-cua-ban.com (hoặc localhost:3000) mà không cần chờ email.

Lưu ý: Sau khi đăng nhập thành công, hãy vào Dashboard Admin, cấu hình lại SMTP cho chuẩn rồi restart lại container bằng 2 lệnh:

docker compose down
docker compose up -d

Tổng hợp các lỗi thường gặp & cách fix

Trong quá trình cài đặt, nếu “nhân phẩm” chưa tốt và gặp lỗi, bạn hãy tra cứu bảng thần chú này:

  • Lỗi: nodesource ... does not have a Release file
    • Nguyên nhân: Repo Node cũ trong Image bị chết.
    • Khắc phục: Dùng lệnh sed (Fix 1) ở mục Tải mã nguồn để xóa file list lỗi.
  • Lỗi: exec: "/usr/bin/bash": not found
    • Nguyên nhân: Script gọi sai đường dẫn Bash (do khác biệt OS).
    • Khắc phục: Dùng lệnh sed (Fix 2) để tạo symlink ln -s /bin/bash /usr/bin/bash.
  • Lỗi: Could not locate Gemfile
    • Nguyên nhân: Docker build không tìm thấy code.
    • Khắc phục: Thêm dòng target: production vào docker-compose.yml.
  • Lỗi: ActiveRecord::ConnectionNotEstablished ... 127.0.0.1
    • Nguyên nhân: Rails tìm DB ở localhost của container thay vì service postgres.
    • Khắc phục: Sửa .env, đảm bảo DATABASE_URL=postgresql://...@postgres:5432....
  • Lỗi: Errno::EACCES (Permission denied @ dir_s_mkdir)
    • Nguyên nhân: Container không có quyền ghi vào thư mục uploads.
    • Khắc phục: Chạy bộ lệnh chown và chmod ở bước Fix quyền thư mục Upload.
  • Lỗi: PG::UniqueViolation: duplicate key
    • Nguyên nhân: Tài khoản đã tạo rồi nhưng UI báo lỗi ảo.
    • Khắc phục: Vào Rails Console, tìm user (User.find_by_email), rồi chạy lệnh confirm + add_role.

Quy trình update Forem

Vận hành một hệ thống Production không chỉ là cài đặt xong rồi để đó. Bạn cần biết cách cập nhật code mới từ Forem để vá lỗi bảo mật và quan trọng hơn là sao lưu dữ liệu đề phòng sự cố.

Vì đang chạy trên máy ảo Ubuntu (OrbStack) với Docker Compose, quy trình này sẽ thực hiện hoàn toàn trong Terminal của Ubuntu

Mã nguồn Forem thay đổi liên tục. Để cập nhật phiên bản mới nhất từ Github mà không làm hỏng hệ thống hiện tại, hãy làm theo các bước sau:

Bước 1: Chuẩn bị

Truy cập vào thư mục code:

cd ~/forem

Bước 2: Tải code mới nhất về

LƯU Ý:
Vì chúng ta đã sửa file Containerfile bằng lệnh sed trước đó, khi git pull có thể sẽ báo lỗi xung đột hoặc ghi đè. Tốt nhất là reset lại file này, kéo code mới, rồi vá lỗi lại.
# Hủy các thay đổi local để nhận code mới sạch sẽ
git checkout Containerfile

# Kéo code mới nhất từ nhánh main
git pull origin main

Bước 3: Vá lại lỗi Containerfile (Bắt buộc)

Code mới tải về vẫn sẽ dính lỗi cũ (NodeSource & Bash path), bạn phải chạy lại 2 lệnh thần thánh này:

# Fix lỗi NodeSource & Bash Path
sed -i 's/apt update/rm -f \/etc\/apt\/sources.list.d\/nodesource.list \&\& apt update/g' Containerfile
sed -i '/apt-get install -yq --no-install-recommends \\/a \      ln -s /bin/bash /usr/bin/bash && \\' Containerfile

Bước 4: Build và cập nhật Database

Quá trình này sẽ đóng gói code mới vào Image và cập nhật cấu trúc bảng dữ liệu (nếu có thay đổi).

# 1. Build lại Image Production (Mất vài phút)
docker compose build

# 2. Chạy Migration (Cập nhật cấu trúc DB)
# Lưu ý: KHÔNG chạy db:setup hay db:reset ở bước này (sẽ mất dữ liệu)
docker compose run --rm web bundle exec rails db:migrate

# 3. Biên dịch lại giao diện (Assets)
docker compose run --rm web bundle exec rails assets:precompile

Bước 5: Khởi động lại

# Khởi động lại container với image mới
docker compose up -d

# Xóa bớt image cũ cho nhẹ máy
docker image prune -f

Quy trình sao lưu (Backup)

Dữ liệu quan trọng nhất của Forem nằm ở 3 nơi:

  • Database (PostgreSQL): Chứa bài viết, user, comment…
  • Uploads: Chứa ảnh avatar, ảnh bài viết (nằm trong thư mục public/uploads).
  • File cấu hình: File .env.

Dưới đây là quy trình thiết lập backup toàn bộ dữ liệu Forem (Database + Ảnh Uploads + Config) lên Google Drive thông qua công cụ Rclone, chạy tự động hàng ngày.

Bạn thực hiện toàn bộ các bước này trong Terminal của máy ảo Ubuntu.

Cài đặt và cấu hình Rclone

Rclone là công cụ dòng lệnh mạnh mẽ để đồng bộ dữ liệu lên Cloud.

1. Cài đặt Rclone

sudo -v ; curl https://rclone.org/install.sh | sudo bash

Nếu nó báo lỗi “None of the supported tools for extracting zip archives (unzip 7z busybox) were found. Please install one of them and try again.” thì bạn cần cài thêm zip cho nó: sudo apt install zip

2. Cấu hình kết nối Google Drive (Headless Auth)

Vì máy ảo Ubuntu không có trình duyệt để bạn đăng nhập Google, chúng ta cần làm bước này hơi đặc biệt một chút (kết hợp với máy Mac của bạn).

Trên máy ảo Ubuntu:

  1. Gõ lệnh: rclone config
  2. Chọn n (New remote).
  3. Đặt tên: gdrive (hoặc tên gì bạn thích).
  4. Chọn Storage: Tìm số tương ứng với Google Drive (thường là số 22 hoặc quanh đó) và nhập vào.
  5. client_id: Để trống (Enter).
  6. client_secret: Để trống (Enter).
  7. scope: Chọn 1 (Full access).
  8. service_account_file: Để trống (Enter).
  9. Edit advanced config?: Chọn n.
  10. QUAN TRỌNG: Use auto config? -> Chọn n (No).

Rclone sẽ hiện ra một dòng lệnh dài loằng ngoằng dạng rclone authorize "drive" "...". Hãy copy dòng lệnh đó.

Trên máy Mac (Terminal của Mac, không phải VM):

  1. Mở Terminal mới trên Mac.
  2. Dán dòng lệnh vừa copy vào và Enter.
  3. Trình duyệt sẽ bật lên, bạn đăng nhập Google và cấp quyền cho Rclone.
  4. Sau khi xong, Terminal Mac sẽ hiện ra một mã token (dạng {"access_token":...}). Copy toàn bộ mã token này.

Quay lại máy ảo Ubuntu:

  1. Dán mã token vào chỗ config_token>.
  2. Chọn n (Keep this “team drive” configuration).
  3. Chọn y (Yes this is ok).
  4. Chọn q (Quit).

Lúc này bạn đã kết nối thành công Ubuntu với Google Drive.

Viết script backup tự động

Chúng ta sẽ tạo một script để làm các việc: Dump database -> Nén thư mục uploads -> Copy lên GDrive -> Xóa file tạm.

1. Tạo file script

mkdir -p ~/scripts
nano ~/scripts/backup_forem.sh

2. Dán nội dung sau vào file (Sửa lại các đường dẫn cho đúng với user của bạn)

#!/bin/bash

# --- CẤU HÌNH ---
# 1. Tên Rclone Remote bạn vừa tạo
RCLONE_REMOTE="gdrive"
# 2. Thư mục trên Google Drive để lưu backup
GDRIVE_FOLDER="Forem_Backups"
# 3. Thư mục chứa code Forem trên máy ảo
FOREM_DIR="/home/vnrom/forem"
# 4. Thư mục tạm để chứa file backup trước khi up
BACKUP_DIR="/home/vnrom/backups_temp"
# 5. Định dạng ngày tháng
DATE=$(date +%Y-%m-%d_%H-%M-%S)
TARGET_DIR="$BACKUP_DIR/$DATE"

# --- BẮT ĐẦU ---
echo "[$(date)] Bắt đầu backup Forem..."

# Tạo thư mục tạm
mkdir -p "$TARGET_DIR"

# 1. Backup Database (Nén trực tiếp để tiết kiệm dung lượng)
echo "-> Dumping Database..."
cd $FOREM_DIR
# Lưu ý: Dùng -T để tắt TTY (quan trọng khi chạy cron)
docker compose exec -T postgres pg_dump -U forem forem_production | gzip > "$TARGET_DIR/database.sql.gz"

# 2. Backup Uploads (Ảnh, Avatar...)
echo "-> Archiving Uploads..."
# Nén thư mục public/uploads từ trong container web ra ngoài
docker compose exec -T web tar -czf - /opt/apps/forem/public/uploads > "$TARGET_DIR/uploads.tar.gz"

# 3. Backup File Config (.env)
echo "-> Copying Config..."
cp "$FOREM_DIR/.env" "$TARGET_DIR/.env_backup"

# 4. Upload lên Google Drive
echo "-> Uploading to Google Drive..."
# Copy toàn bộ folder ngày hôm nay lên
rclone copy "$TARGET_DIR" "$RCLONE_REMOTE:$GDRIVE_FOLDER/$DATE" --transfers=4

# 5. Dọn dẹp file tạm trên máy ảo (Để không bị đầy ổ cứng)
echo "-> Cleaning up local files..."
rm -rf "$TARGET_DIR"

# 6. (Tùy chọn) Xóa backup cũ trên Drive quá 30 ngày
echo "-> Deleting old backups on Drive (>30 days)..."
rclone delete "$RCLONE_REMOTE:$GDRIVE_FOLDER" --min-age 30d --rmdirs

echo "[$(date)] ✅ Backup hoàn tất!"

3. Cấp quyền thực thi cho script

chmod +x ~/scripts/backup_forem.sh

Chạy thử và cronjob

1. Chạy thử bằng tay

Hãy chạy lệnh sau để xem script có hoạt động không:

~/scripts/backup_forem.sh

Nếu nó chạy một mạch, báo “✅ Backup hoàn tất!” và bạn thấy folder mới xuất hiện trên Google Drive của mình, thì mọi thứ đã chuẩn.

2. Cài đặt tự động chạy hàng ngày (Cronjob)

Chúng ta sẽ cài đặt để nó tự chạy vào 3:00 sáng mỗi ngày.

Gõ lệnh:

crontab -e

Thêm dòng sau vào cuối file:

~/scripts/backup_forem.sh# Chạy backup lúc 3h sáng hàng ngày, ghi log ra file để debug nếu lỗi
0 3 * * * /home/vnrom/scripts/backup_forem.sh >> /home/vnrom/scripts/backup.log 2>&1

(Nhớ thay /home/vnrom bằng đường dẫn user thực tế của bạn nếu khác).

Bây giờ hệ thống Forem của bạn đã có cơ chế bảo vệ kép:

  1. Dữ liệu nằm trên máy ảo Ubuntu (ổ định, không lỗi permission).
  2. Backup tự động lên Google Drive mỗi ngày.
  3. Tự động xóa bản cũ sau 30 ngày để không bị full Drive.

Bạn có thể yên tâm phát triển cộng đồng rồi!

Quy trình khôi phục (Restore)

Chỉ thực hiện khi bạn chuyển server, hoặc lỡ tay xóa nhầm dữ liệu. Cảnh báo: Dữ liệu hiện tại sẽ bị ghi đè.

Giả sử bạn đã có 2 file backup: forem_db.sqluploads.tar.gz.

Bước 1: Dừng hệ thống (Tránh xung đột)

docker compose stop web sidekiq

Bước 2: Khôi phục Database

Lệnh này sẽ xóa DB hiện tại và nạp lại từ file backup.

# 1. Xóa database cũ và tạo lại database trắng
docker compose exec postgres dropdb -U forem forem_production
docker compose exec postgres createdb -U forem forem_production

# 2. Nạp dữ liệu từ file SQL
# (Lưu ý dấu < để truyền file vào)
cat ~/backups/forem_db.sql | docker compose exec -T postgres psql -U forem forem_production

Bước 3: Khôi phục File Uploads

# 1. Copy file nén vào trong container
docker cp ~/backups/uploads.tar.gz forem_web:/tmp/uploads.tar.gz

# 2. Giải nén vào đúng vị trí
docker compose exec web tar -xzf /tmp/uploads.tar.gz -C /opt/apps/forem/public/uploads/

# 3. QUAN TRỌNG: Fix lại quyền (Permission) sau khi restore
# Nếu không làm bước này, ảnh sẽ không hiện hoặc không up mới được
docker compose exec -u root web chown -R 1000:1000 /opt/apps/forem/public/uploads
docker compose exec -u root web chmod -R 777 /opt/apps/forem/public/uploads

Bước 4: Khởi động lại

docker compose start

Tóm tắt các lệnh hay dùng hàng ngày

Tác vụLệnh
Khởi động serverdocker compose up -d
Dừng serverdocker compose stop
Xem logs (Web)docker compose logs -f web
Xem logs (Email/Job)docker compose logs -f sidekiq
Vào Console Railsdocker compose exec web bundle exec rails c
Check kết nối mạngdocker compose exec web curl -I https://google.com

Vậy là bạn đã có trong tay trọn bộ bí kíp để vận hành Forem an toàn và bền vững!

Hy vọng hướng dẫn này giúp bạn triển khai thành công Forem trên máy Mac của bạn.

Duy Nghiện
Hãy làm khán giả, đừng làm nhân vật chính :)

You may also like

Nhận thông báo qua email
Nhận thông báo cho
guest

0 Bình luận
Mới nhất
Cũ nhất Nhiều like nhất
Phản hồi nội tuyến
Xem tất cả bình luận