If you have tried deploying Forem (the open-source software powering DEV.to) on a Mac with an M1, M2, M3 or M4 chip using Docker Desktop, you likely faced a nightmare of errors: nokogiri build failures, architecture mismatches (amd64 vs arm64), file permission issues, and unbearable slowness.
The solution isn’t to fight Docker Desktop, but to bypass the architectural friction entirely. In this guide, we will deploy a production-ready Forem instance inside a lightweight Ubuntu Virtual Machine managed by OrbStack.
This approach offers native Linux performance, eliminates file system bridge slowness, and resolves the infamous /usr/bin/bash and permission errors.

Prerequisites
- OrbStack: A fast, light, and easy way to run Docker containers and Linux machines on macOS. Download here.
- VS Code (Optional but recommended): For easy file editing inside the VM.
Setting up the Environment
We will create a dedicated Ubuntu environment. This ensures your Forem instance runs on a standard Linux file system (ext4), avoiding the performance penalties of macOS file sharing.
1. Create the Virtual Machine
Open your Mac Terminal and run:
# Create an Ubuntu 24.04 (Noble) machine named 'forem-server'
orb create ubuntu:noble forem-server2. Enter the VM
From now on, all commands must be run inside this shell:
orb -m forem-server3. Install Docker Engine (Inside the VM)
Although OrbStack provides Docker, installing the native Docker engine inside the VM ensures a completely isolated production environment.
# Update system
sudo apt-get update && sudo apt-get upgrade -y
# Install Docker and Compose
sudo apt-get install -y docker.io docker-compose-v2
# Add your user to the docker group (to run commands without sudo)
sudo usermod -aG docker $USER
newgrp dockerGetting the Code & Patching Legacy Issues
Forem’s default configuration currently has some legacy settings that break on modern systems. We will patch them automatically.
1. Clone the Repository
Important: Clone this into the VM’s home directory. Do not clone it on your Mac and mount it.
cd ~
git clone https://github.com/forem/forem.git
cd forem2. Patch the Containerfile
We need to fix two critical issues in the default Dockerfile:
- NodeSource Error: The old Node.js repository configuration causes 404 errors during build.
- Bash Path Error: Forem scripts expect
/usr/bin/bash, but Debian/Ubuntu uses/bin/bash.
Run these sed commands to patch the file automatically:
# Fix 1: Remove the broken NodeSource list before running apt-get update
sed -i 's/apt update/rm -f \/etc\/apt\/sources.list.d\/nodesource.list \&\& apt update/g' Containerfile
# Fix 2: Create a symlink for bash to satisfy Forem's scripts
sed -i '/apt-get install -yq --no-install-recommends \\/a \ ln -s /bin/bash /usr/bin/bash && \\' ContainerfileProduction Configuration
We will skip the development setup and go straight to a lean Production setup.
1. Configure .env
Create a new .env file:
nano .envPaste the following configuration. This is optimized for a standalone production instance.
# --- APP IDENTITY ---
# Change this to your real domain (e.g., ai.addrom.com)
APP_DOMAIN="your-domain.com"
APP_PROTOCOL="https://"
COMMUNITY_NAME="addROM Community"
DEFAULT_EMAIL="[email protected]" # Must match SMTP_USER_NAME
# --- SECRETS (Generate new random strings for these!) ---
FOREM_OWNER_SECRET="long_random_string_1"
SECRET_KEY_BASE="long_random_string_2"
# --- DATABASE & REDIS ---
# Note: We use service names 'postgres' and 'redis', NOT 'localhost'
DATABASE_URL="postgresql://forem:forem@postgres:5432/addrom_forem"
DATABASE_NAME="addrom_forem"
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 CONFIGURATION (Example: Larksuite/Gmail) ---
# We use port 587 (STARTTLS) for maximum stability
SMTP_ADDRESS="smtp.larksuite.com"
SMTP_PORT="587"
SMTP_DOMAIN="your-domain.com"
SMTP_USER_NAME="[email protected]"
SMTP_PASSWORD="your_app_password"
SMTP_AUTHENTICATION="login"
SMTP_TLS=false
SMTP_ENABLE_STARTTLS_AUTO=true
# Optional: Bypass SSL verification if you encounter certificate errors
SMTP_OPENSSL_VERIFY_MODE="none"
# --- SECURITY ---
FORCE_SSL_IN_RAILS="true"2. Configure docker-compose.yml
Delete the existing file and replace it with this clean Production version.
Crucial: Note the target: production line. This ensures the code is copied inside the image, fixing the “Gemfile not found” error.
nano docker-compose.yml:
services:
postgres:
image: postgres:13
container_name: forem_postgres
restart: always
environment:
POSTGRES_USER: forem
POSTGRES_PASSWORD: forem
POSTGRES_DB: addrom_forem
volumes:
- db_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "forem"]
interval: 10s
timeout: 5s
retries: 5
env_file:
- .env
redis:
image: redis:6.2-alpine
container_name: forem_redis
restart: always
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
env_file:
- .env
web:
build:
context: .
dockerfile: Containerfile
target: production # <--- ESSENTIAL
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:
# Persist uploads and assets
- 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 and Initialization
This is where the magic happens. Run these commands sequentially.
1. Build the Docker Images:
docker compose build2. Initialize the Database:
We use bundle exec to ensure the command runs within the correct Ruby context inside the container.
docker compose run --rm web bundle exec rails db:setup3. Fix Permissions (Crucial step):
Docker containers often run as a specific user (forem/1000) but volumes might be owned by root. This fixes the Errno::EACCES error when uploading images.
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. Precompile Assets:
Required for CSS and JS to load correctly in production mode.
docker compose run --rm web bundle exec rails assets:precompile5. Start the Server:
docker compose up -dCreating the Super Admin (The Reliable Way)
Don’t rely on the registration form initially (SMTP might fail, or you might hit 404s). Create your admin account directly via the Rails Console.
- Enter the console:
docker compose exec web bundle exec rails c- Run this Ruby code block:
# Create the user
u = User.new(
name: "Super Admin",
username: "admin",
email: "[email protected]",
password: "StrongPassword123!",
password_confirmation: "StrongPassword123!"
)
u.save!
# Confirm email automatically (Bypass SMTP)
u.confirm
# Grant Super Admin privileges
u.add_role(:admin)
u.add_role(:super_admin)
u.save!
exitNow navigate to http://<VM-IP-Address>:3000 or http://localhost:3000. You can log in immediately!
Maintenance (Update, Backup, Restore)
Updating Forem
Since we modified the Containerfile, we need to be careful when pulling updates.
# 1. Reset file changes to allow git pull
git checkout Containerfile
# 2. Get latest code
git pull origin main
# 3. Re-apply patches (Run the sed commands from Part 2 again)
# ... [Insert sed commands here] ...
# 4. Rebuild and Migrate
docker compose build
docker compose run --rm web bundle exec rails db:migrate
docker compose run --rm web bundle exec rails assets:precompile
docker compose up -dBackups
Run these commands to backup your data to the VM’s home directory.
# Database
docker compose exec -t postgres pg_dump -U forem addrom_forem > ~/forem_backup.sql
# Uploads
docker compose exec -t web tar -czf /tmp/uploads.tar.gz -C /opt/apps/forem/public/uploads .
docker cp forem_web:/tmp/uploads.tar.gz ~/uploads_backup.tar.gzTroubleshooting Cheat Sheet
| Error Message | Cause | Solution |
E: The repository ... nodesource ... does not have a Release file | Old Node.js repo in the base image. | Run the sed command to remove nodesource.list in Containerfile. |
exec: "/usr/bin/bash": stat /usr/bin/bash: no such file | Path mismatch (Debian vs script expectations). | Run the sed command to symlink /bin/bash to /usr/bin/bash. |
Could not locate Gemfile | Docker built an empty development image. | Ensure target: production is in your docker-compose.yml. |
ActiveRecord::ConnectionNotEstablished ... 127.0.0.1 | Rails is trying to connect to localhost DB. | Update .env: DATABASE_URL=postgresql://...@postgres:5432... |
Errno::EACCES (Permission denied @ dir_s_mkdir) | Container user cannot write to volume. | Run the chown -R 1000:1000 commands in Part 4. |
Net::ReadTimeout (Email) | Wrong SMTP port or SSL mode. | Use Port 587 + SMTP_TLS=false + SMTP_ENABLE_STARTTLS_AUTO=true. |
PG::UniqueViolation: duplicate key | User created but UI showed error. | Use Rails Console (User.find_by...) to find and promote the user. |
By following this guide, you leverage the power of Linux on your Apple Silicon Mac via OrbStack, resulting in a stable, high-performance Forem community. Happy coding!








