# Ứng dụng Ruby và Rails

> Cài Ruby, deploy Rails, Sinatra hay ứng dụng Rack khác từ Git với Bundler và Puma, precompile asset, kết nối database và đặt biến môi trường.

Source: https://zopanel.net/vi/docs/ruby  
Updated: 2026-10-09

Ứng dụng Ruby chạy trên ZoPanel như ứng dụng server: ZoPanel cài gem bằng Bundler, build bản release, chạy ứng dụng như một dịch vụ của tài khoản hosting phía sau nginx và chuyển sang bản mới không gián đoạn. Trang này dành cho Ruby on Rails, ứng dụng Rack (Sinatra, Hanami…) và server Ruby thuần; cổng, phát hành blue/green và log hoạt động như mô tả trong [Ứng dụng Node.js và Python](/vi/docs/apps-node-python).

## Cài Ruby lên máy chủ

Quản trị viên cài Ruby một lần:

1. Mở **Runtime**.
2. Trong thẻ **Ruby** ("Ruby kèm Bundler, cho Rails và Sinatra."), bấm **Cài đặt**.

ZoPanel cài Ruby của hệ điều hành kèm Bundler và các thư viện mà gem native cần:

```text
ruby-full ruby-bundler build-essential libyaml-dev zlib1g-dev libssl-dev
libpq-dev libsqlite3-dev default-libmysqlclient-dev pkg-config
```

Nhờ vậy các gem như `pg`, `mysql2`, `sqlite3`, `psych`, `nokogiri` biên dịch được mà không cần thêm bước nào. Sau khi cài, thẻ hiển thị phiên bản Ruby.

Mỗi máy chủ có một bản Ruby, chính là bản hệ điều hành cung cấp:

| Hệ điều hành | Ruby | Rails mới nhất chạy được |
| --- | --- | --- |
| Ubuntu 22.04 | 3.0 | Rails 7.1 |
| Debian 12 | 3.1 | Rails 7.2 |
| Ubuntu 24.04 | 3.2 | Rails 8 |
| Debian 13 | 3.3 | Rails 8 |

Rails 8.0 cần Ruby 3.2 trở lên, Rails 7.2 cần Ruby 3.1 trở lên ([hướng dẫn nâng cấp Rails](https://guides.rubyonrails.org/upgrading_ruby_on_rails.html)). Dòng `ruby "x.y"` trong `Gemfile` phải khớp Ruby của máy chủ, nếu không Bundler sẽ dừng. Ô **Phiên bản** và `Gemfile` không chọn được Ruby khác. Mọi log build đều bắt đầu bằng Ruby đang dùng, ví dụ "Using the system Ruby 3.2.3", và thêm cảnh báo khi `Gemfile` yêu cầu phiên bản khác. Nếu ứng dụng cần phiên bản Ruby khác, hãy deploy bằng Dockerfile (xem [Deploy bằng Docker](/vi/docs/docker-deploy)).

## Deploy

1. Ở **Website → Thêm website**, chọn **Git deploy**, nhập **Địa chỉ repository**, **Nhánh** (và **Thư mục gốc** với monorepo) rồi bấm **Tạo website**. Với website có sẵn, dùng tab **Deploy**.
2. ZoPanel nhận diện Ruby qua `Gemfile` và đề xuất các lệnh bên dưới.
3. Khai báo cấu hình trong **Biến môi trường** rồi bấm **Lưu & deploy lại**.

### Lệnh được nhận diện

Mọi ứng dụng Ruby dùng chung lệnh cài đặt. Gem được cài vào bản release (`vendor/bundle`), không cài cho toàn hệ thống, và bỏ qua nhóm development, test:

```bash
bundle config set --local path vendor/bundle && bundle config set --local without 'development test' && bundle install --jobs 4
```

| Nhận diện là | Khi nào | Lệnh build | Lệnh chạy |
| --- | --- | --- | --- |
| Ruby on Rails | Có `config/application.rb` hoặc `Gemfile` có `rails` | `RAILS_ENV=production SECRET_KEY_BASE=precompile bundle exec rails assets:precompile`, chỉ khi có `app/assets/config/manifest.js` hoặc `app/javascript` | `RAILS_ENV=production bundle exec rails server -b 127.0.0.1 -p $PORT` |
| Rack (Sinatra, Hanami…) | Có `config.ru` | – | `bundle exec rackup -o 127.0.0.1 -p $PORT -E production` |
| Ruby | Các trường hợp khác | – | `bundle exec ruby app.rb -o 127.0.0.1 -p $PORT` (cần kiểm tra lại) |

Với Rails, **Đường dẫn lưu trữ cố định** được đặt là `storage` và `log`, log deploy ghi chú "Set SECRET_KEY_BASE and DATABASE_URL in the environment variables". Dòng `web:` trong `Procfile` luôn được ưu tiên làm lệnh chạy.

Muốn đổi lệnh, tắt **Tự nhận diện** trong thẻ **Build & chạy**, sửa rồi bấm **Lưu & deploy**.

### Puma

`rails server` khởi động Puma, server mặc định của ứng dụng Rails mới (`gem "puma"` trong `Gemfile`). Tuỳ chọn `-b` và `-p` gắn Puma vào `127.0.0.1:$PORT`. Số thread lấy từ `config/puma.rb` (`RAILS_MAX_THREADS`, mặc định 3 ở Rails 8). Muốn chạy Puma trực tiếp, có thể dùng:

```bash
bundle exec puma -C config/puma.rb -b tcp://127.0.0.1:$PORT -e production
```

Với ứng dụng Rack dùng Rack 3 (Sinatra 4 trở lên), lệnh `rackup` nằm trong gem riêng: thêm `gem "rackup"` và `gem "puma"` vào `Gemfile` ([README của Sinatra](https://sinatrarb.com/intro.html)).

## Biến môi trường

Đặt các biến sau trong **Biến môi trường** ở tab **Deploy**. Chúng dùng được khi build và khi chạy; `PORT` được đặt tự động.

| Biến | Giá trị |
| --- | --- |
| `SECRET_KEY_BASE` | Chuỗi bí mật ngẫu nhiên dài, ví dụ kết quả của `bin/rails secret` hoặc `openssl rand -hex 64`. Rails dùng giá trị này thay cho giá trị trong credentials đã mã hoá. |
| `RAILS_MASTER_KEY` | Chỉ cần khi ứng dụng đọc giá trị khác từ `config/credentials.yml.enc`. |
| `DATABASE_URL` | Chuỗi kết nối (xem bên dưới). |
| `RAILS_LOG_TO_STDOUT` | `1` với ứng dụng cũ hơn Rails 7.1, để log hiện trong **Log ứng dụng**. Từ Rails 7.1, log mặc định ghi ra stdout. |
| `SOLID_QUEUE_IN_PUMA` | `1` để chạy job Solid Queue ngay trong Puma (theo `config/puma.rb` mặc định của Rails 8). |

Lệnh build chỉ đặt `SECRET_KEY_BASE` giả cho bước `assets:precompile`, nên bước precompile không cần khoá thật.

## Database

**SQLite (mặc định của Rails 8).** Ứng dụng Rails 8 mới lưu database production trong `storage/` (`storage/production.sqlite3` cùng các database cache, queue, cable). `storage` là đường dẫn lưu trữ cố định nên dữ liệu được giữ qua mọi lần deploy. Ngoài migration, không cần làm gì thêm.

**MariaDB hoặc PostgreSQL.** Tạo database ở **Cơ sở dữ liệu** rồi đặt `DATABASE_URL`; host là `127.0.0.1` ([cấu hình database của Rails](https://guides.rubyonrails.org/configuring.html#configuring-a-database)):

```dotenv
# MariaDB với gem mysql2
DATABASE_URL=mysql2://alice_app:mat-khau@127.0.0.1:3306/alice_app
# MariaDB với gem trilogy (Rails 7.1+)
DATABASE_URL=trilogy://alice_app:mat-khau@127.0.0.1:3306/alice_app
# PostgreSQL với gem pg
DATABASE_URL=postgresql://alice_app:mat-khau@127.0.0.1:5432/alice_app
```

Mã hoá URL các ký tự đặc biệt trong mật khẩu (ví dụ `@` thành `%40`).

### Migration

Migration không tự chạy. Tắt **Tự nhận diện** rồi thêm vào cuối lệnh build:

```bash
RAILS_ENV=production SECRET_KEY_BASE=precompile bundle exec rails assets:precompile && RAILS_ENV=production bundle exec rails db:prepare
```

`db:prepare` tạo database còn thiếu và chạy các migration đang chờ. Bước build chạy trước khi bản mới được đưa lên, trong lúc bản cũ vẫn phục vụ khách, nên hãy viết migration sao cho bản đang chạy vẫn hoạt động được.

## SSL, file tĩnh và file tải lên

- nginx xử lý SSL và gửi header `X-Forwarded-Proto`. Rails 8 bật `config.assume_ssl` và `config.force_ssl` ở production, Rails 7.1 bật `config.force_ssl`: hãy giữ **SSL miễn phí (Let's Encrypt)**, nếu không cookie bảo mật và chuyển hướng sẽ làm hỏng đăng nhập qua HTTP thường.
- nginx chuyển mọi request tới ứng dụng, kể cả file trong `public/`. Từ Rails 7.1, Rails tự phục vụ các file này; với Rails 7.0 hãy đặt `RAILS_SERVE_STATIC_FILES=1`.
- Active Storage dùng disk local sẽ ghi vào `storage/`, vốn là đường dẫn lưu trữ cố định. Thư mục khác cần giữ lại giữa các bản, ví dụ `public/uploads`, hãy thêm vào **Đường dẫn lưu trữ cố định**.

## Release, log và khởi động lại

- Bản mới khởi động ở slot đang rảnh và phải trả lời **Đường dẫn health check** (mặc định `/`) với mã dưới 500 trong vòng 90 giây; chuyển hướng vẫn tính là đạt. Từ Rails 7.1 có sẵn route `/up` để dùng làm health check. Chỉ khi đó nginx mới chuyển sang và tiến trình cũ mới dừng.
- **Các bản release** giữ 5 bản build gần nhất để **Rollback** (database không được rollback).
- **Log ứng dụng** hiển thị 400 dòng cuối ứng dụng ghi ra; bật **Trực tiếp** để theo dõi. Thư mục `log/` cũng được lưu trữ cố định.
- Dịch vụ tự khởi động lại khi Puma thoát. RAM và CPU được tính vào gói của tài khoản.

## Xử lý sự cố

| Thông báo hoặc hiện tượng | Cách xử lý |
| --- | --- |
| `bundle: command not found` ở bước cài đặt | Nhờ quản trị viên cài Ruby ở **Runtime**. |
| "Your Ruby version is …, but your Gemfile specified …" | `Gemfile` yêu cầu Ruby khác bản của máy chủ (log build cảnh báo điều này trước bước cài đặt). Nới dòng `ruby` (ví dụ `ruby "~> 3.2"`, hoặc xóa dòng đó), hoặc deploy bằng Dockerfile. |
| Một gem không biên dịch được | Đọc log build trong **Tác vụ**; các header thông dụng đã được cài cùng Ruby. Thư viện native khác cần quản trị viên cài thêm. |
| "the app did not respond on port … within 90s (it must listen on $PORT)" | Gắn vào `127.0.0.1:$PORT` (`-b 127.0.0.1 -p $PORT`). |
| "the app exited during startup" | Đọc **Log ứng dụng**: thường do thiếu `SECRET_KEY_BASE`, `DATABASE_URL`, hoặc chưa chạy migration. |
| `ActiveRecord::PendingMigrationError` | Thêm `bundle exec rails db:prepare` vào lệnh build (xem phần Migration). |
| Đăng nhập lặp vòng hoặc mất cookie | Website chạy không có SSL trong khi Rails ép SSL. Hãy cấp chứng chỉ (xem [Chứng chỉ SSL](/vi/docs/ssl)). |
| Repository bị nhận diện là Node.js | File `package.json` ở thư mục gốc (jsbundling, cssbundling) được ưu tiên hơn `Gemfile`. Tắt **Tự nhận diện**, chọn **Runtime** là `ruby`, **Loại** là **Ứng dụng server (tiến trình)** và nhập các lệnh Rails ở trên; thêm `npm ci &&` vào đầu lệnh build nếu asset cần Node.js. |

## Xem thêm

- [Ứng dụng Node.js và Python](/vi/docs/apps-node-python)
- [Git deploy](/vi/docs/git-deploy)
- [Cơ sở dữ liệu](/vi/docs/databases)
- [Chứng chỉ SSL](/vi/docs/ssl)
- [Rails guides](https://guides.rubyonrails.org/)
