Đợ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.
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ường gặp rất nhiều vấn đề về hiệu năng và tương thích (lỗi nokogiri, lỗi kiến trúc amd64 vs arm64, lỗi quyền ghi file…).
Giải pháp tối ưu nhất không phải là chạy Docker trực tiếp trên Mac, mà là chạy một máy ảo Linux (Ubuntu) nhẹ thông qua OrbStack, sau đó triển khai Forem bên trong đó. Bài viết này sẽ hướng dẫn bạn từng bước, bao gồm cả cách “vá” những lỗi kinh điển mà tài liệu chính thức chưa cập nhật.

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):
orb create ubuntu:noble forem-server2. Truy cập vào máy ảo
Từ giờ trở đi, mọi lệnh chúng ta sẽ gõ bên trong máy ảo này:
orb -m forem-server3. Cài đặt Docker & Docker Compose bên trong VM
Dù OrbStack có Docker, nhưng để môi trường khép kín chuẩn Production, ta nên cài Docker Engine cho Ubuntu:
# 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 forem2. “Vá” lỗi Containerfile
Image gốc của Forem đang dính 2 lỗi lớn: Repo Node.js bị chết (gây lỗi 404 khi build) và thiếu đường dẫn Bash. Chạy 2 lệnh sau để tự động sửa file Containerfile:
# 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 && \\' Containerfile3. Cấu hình file .env (Production)
Tạo file .env:
nano .envVà dán nội dung tối ưu sau:
# APP IDENTITY
APP_DOMAIN="vnrom.net"
APP_PROTOCOL="https://"
COMMUNITY_NAME="vnROM 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.net/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="vnrom.net"
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 (Production)
Xóa file cũ và thay bằng nội dung này. Lưu ý dòng target: production là chìa khóa để fix lỗi thiếu Gemfile.
nano docker-compose.ymlCopy và paste nội dung sau:
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 (The Magic Moment)
Chạy lần lượt các lệnh sau:
1. Build Image:
docker compose build2. Khởi tạo Database:
Lưu ý dùng bundle exec thay vì bin/rails để tránh lỗi đường dẫn.
docker compose run --rm web bundle exec rails db:setup3. Fix quyền thư mục Upload (Bắt buộc):
Nếu không làm bước này, bạn sẽ không thể upload avatar (Lỗi Errno::EACCES).
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/public4. Biên dịch giao diện:
docker compose run --rm web bundle exec rails assets:precompile5. Khởi động hệ thống:
docker compose up -dTạo tài khoản Admin
Rất nhiều trường hợp bạn đăng ký trên Web sẽ bị lỗi do SMTP chưa thông, hoặc lỗi ghi file. Cách chắc chắn nhất là tạo Admin qua dòng lệnh:
- Truy cập Console:
docker compose exec web bundle exec rails c- 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!
exitBâ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.
Sau khi đăng nhập thành công thì vào Dashboard Admin, sau đó cập nhật lại SMTP và restart lại container với 2 lệnh sau:
docker compose down
docker compose up -dTổng hợp các lỗi thường gặp & cách fix
Trong quá trình cài đặt, nếu bạn gặp lỗi, hãy tra cứu bảng bên dưới:
| Lỗi (Error Log) | Nguyên nhân | Cách khắc phục |
nodesource ... does not have a Release file | Repo Node cũ trong Image bị die. | Dùng lệnh sed ở Phần 2, Mục 2 để xóa file list bị lỗi trong Containerfile. |
exec: "/usr/bin/bash": not found | Script gọi sai đường dẫn Bash (Do khác biệt OS). | Dùng lệnh sed tạo symlink ln -s /bin/bash /usr/bin/bash trong Containerfile. |
Could not locate Gemfile | Docker build thiếu code nguồn. | Thêm dòng target: production vào docker-compose.yml. |
ActiveRecord::ConnectionNotEstablished ... 127.0.0.1 | Rails tìm DB ở localhost thay vì container postgres. | Sửa .env: DATABASE_URL=postgresql://...@postgres:5432.... |
Errno::EACCES (Permission denied @ dir_s_mkdir) | Container không có quyền ghi vào thư mục uploads. | Chạy bộ lệnh chown và chmod ở Phần 3, Bước 3. |
Net::ReadTimeout (khi gửi mail) | Sai cổng SMTP hoặc chế độ SSL/TLS. | Thử đổi cổng 587 (StartTLS) hoặc 465 (TLS=true). Kiểm tra DEFAULT_EMAIL phải trùng SMTP_USER. |
PG::UniqueViolation: duplicate key | Tài khoản đã được tạo nhưng UI báo lỗi ảo. | Vào Rails Console, tìm user đó (User.find_by_email), và chạy lệnh confirm + add_role. |
exec: "nc": executable file not found | Image production không có lệnh nc. | Dùng lệnh Ruby hoặc Curl để test mạng (Xem lại chat history). |
Bảo trì hệ thống (Update, Backup & Restore)
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 (orb -m forem-server).
Quy trình update Forem
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 ~/foremBướ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 && \\' ContainerfileBướ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:precompileBướ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 -fQuy 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.
Bạn nên tạo một thư mục backup riêng trong máy ảo:
mkdir -p ~/backups1. Sao lưu Database (PostgreSQL)
Chúng ta dùng lệnh pg_dump từ bên trong container để xuất dữ liệu ra file SQL.
# Tạo file backup có tên kèm ngày tháng (ví dụ: forem_db_2023-10-27.sql)
docker compose exec -t postgres pg_dump -U forem forem_production > ~/backups/forem_db_$(date +%F).sql2. Sao lưu file Uploads (Ảnh)
Copy toàn bộ thư mục uploads từ trong container ra ngoài máy ảo.
# Nén thư mục uploads lại thành file .tar.gz
docker compose exec -t web tar -czf /tmp/uploads.tar.gz -C /opt/apps/forem/public/uploads .
# Copy file nén ra ngoài
docker cp forem_web:/tmp/uploads.tar.gz ~/backups/uploads_$(date +%F).tar.gz3. Sao lưu file .env
cp ~/forem/.env ~/backups/.env_backupMẸO:
Bạn có thể copy thư mục ~/backups này từ máy ảo Ubuntu ra máy Mac để lưu trữ an toàn bằng cách mở Finder -> OrbStack -> Chọn máy ảo -> Copy thư mục.
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.sql và uploads.tar.gz.
Bước 1: Dừng hệ thống (Tránh xung đột)
docker compose stop web sidekiqBướ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_productionBướ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/uploadsBước 4: Khởi động lại
docker compose startTóm tắt các lệnh hay dùng hàng ngày
| Tác vụ | Lệnh |
| Khởi động server | docker compose up -d |
| Dừng server | docker compose stop |
| Xem logs (Web) | docker compose logs -f web |
| Xem logs (Email/Job) | docker compose logs -f sidekiq |
| Vào Console Rails | docker compose exec web bundle exec rails c |
| Check kết nối mạng | docker 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.








