PostnHost is an open source CMS engine built with Rails, Hotwire, and TailwindCSS. Write, translate, and publish articles with a rich text editor, full version history, and proper SEO that works out of the box.
This repository is the self-hosted version: a RailsΒ 8 app built around the PostnHost engine, packaged with SQLite, Solid Queue, Solid Cache, Litestream, and Tailwind. Install, generators, and overrides are covered in the engine README.
- π Rich Text Editor - TipTap-based WYSIWYG editor
- π Multilingual - Translatable articles, i18n support, and locale-aware SEO metadata
- πΌοΈ Image Management - Optimized multisize WebP everywhere
- π Version History - Article versions history and rollback options
- π€ Suggested Articles - Manual picks plus automatic suggestions from related categories
- β° Post Scheduling - Schedule publication in the chosen timezone
- π Localized SEO - Localized routes, language switcher, sitemap, and hreflang tags
- βοΈ Settings - Manage the canonical site URL, pagination, key copy, and assets from the dashboard
- β‘ Hotwire-powered - Fast, modern UI with Turbo Frames/Streams and Stimulus
- ποΈ Single SQLite database - App data, Solid Cache, Solid Queue, and Action Cable in one file
- π Backups - Litestream replication to S3-compatible storage
- π Session-based auth - Built-in admin authentication
- π₯ Authors - Multiple CMS users, author profiles, and per-article co-author bylines
- Tech stack
- Asset compatibility
- Quick start
- Configuration
- Customizing the host app
- Robots.txt and sitemap
- Testing
- Production checklist
- Docker development
- License
| Component | Technology |
|---|---|
| Framework | Rails |
| Database | SQLite3 |
| Frontend | Hotwire (Turbo + Stimulus), TailwindCSS |
| Editor | Tiptap |
| File storage | CarrierWave (S3 compatible) |
| Background jobs | Solid Queue |
| Caching | Solid Cache |
| Backups | Litestream |
This self-hosted application uses Propshaft for asset serving and Tailwind CSS for host overrides. Its CSS build combines engine and host view sources into one scoped postnhost/application.css that transparently overrides the packaged engine asset, so copied templates can use ordinary Tailwind classes without competing builds. The engine ships a prebundled JavaScript ES module.
The engine also supports Sprockets and host applications using import maps. See the engine asset compatibility matrix for details.
With the versions from mise.toml plus SQLite and libvips installed:
git clone https://github.com/postnhost/postnhost-app.git
cd postnhost-app
mise install
gem install bundler
corepack enable
bin/setupThe packageManager field in package.json makes Corepack use the repository's pinned Yarn version. No global Yarn installation is required.
bin/rails db:seedLoads required reference data fromdb/seeds.rb:- Adds 9
Languagerecords (English default, plus French, German, Japanese, Korean, Portuguese, Polish, Spanish, Russian)
- Adds 9
If there are no CMS users yet, visit /onboarding to set up the first admin.
Sample categories and articles are optional and can be added from that setup flow after the administrator account is created.
Alternatively, create a CMS user interactively from the terminal:
bin/rails g postnhost:userThe generator prompts for the name, email, password, and password confirmation.
Full local setup, architecture, backup/restore, and troubleshooting: DEVELOPMENT.md.
Edit config/initializers/postnhost.rb:
Postnhost.configure do |config|
# Optional
# config.site_url = "https://example.com"
# config.public_page_size = 12
# config.default_timezone = "UTC"
# config.openai_api_key = "sk-..."
# config.openai_gpt_model = "gpt-5.6-luna"
# config.aws_access_key_id = "AKIA..."
# config.aws_secret_access_key = "..."
# config.aws_region = "us-east-1"
# config.aws_bucket = "my-bucket"
# config.aws_endpoint_url_s3 = "https://fly.storage.tigris.dev"
endThe initializer provides defaults for site URL, pagination, and timezone. Values saved under Dashboard β Settings take priority. If both site URL sources are blank, public URLs use the incoming request origin.
For localized public pages (/:locale, language switcher, postnhost.public strings), configure I18n in config/application.rb. You must set English as the fallback locale so any key missing in a non-English YAML file still resolves (engine + host locale files are merged; gaps should fall back to English):
config.i18n.default_locale = :en
config.i18n.available_locales = %i[en fr de ja ko pt pl es ru] # add codes as you add Language records and locale files
config.i18n.fallbacks = [:en]Without config.i18n.fallbacks = [:en], visitors can see missing translations or blank UI where a locale file omits a key. See Host app i18n in the engine README for locale files and bin/rails g postnhost:locale.
bin/rails credentials:editpostnhost:
openai_access_token: ...
openai_gpt_model: gpt-5.6-luna
aws_access_key_id: AKIA...
aws_secret_access_key: ...
aws_region: us-east-1
aws_bucket_name: my-bucket
aws_endpoint_url_s3: https://s3.us-east-1.amazonaws.com
# Optional: Litestream SQLite backups
litestream_bucket: your-backup-bucket
litestream_endpoint: fly.storage.tigris.dev- Views β
bin/rails g postnhost:views --views-scope=minimalor--views-scope=full(copies toapp/views/postnhost/). Add ordinary Tailwind classes and keep the CSS watcher running throughbin/dev. Details are under Customizing templates in the gem README. - Favicon and PWA icons β Replace files under
public/; see Replacing favicon. - Logo / default OG image β Dashboard settings and/or assets; brand files for this repo live under
app/assets/images/(for examplelogo.webp,og-image.webp). - i18n and static pages β See Host app i18n and Static pages in the gem README.
Update public/robots.txt so Sitemap: matches your deployment. PostnHost generates sitemap.xml from live public content; URLs use the dashboard site URL when present, then config.site_url, then the incoming request origin.
Mounted at /:
Sitemap: https://your-domain.com/sitemap.xmlMounted at /blog:
Sitemap: https://your-domain.com/blog/sitemap.xmlHost app (from repository root):
bundle exec rspec spec/requests/engine_mount_smoke_spec.rb
bundle exec rspec spec/system/authentication_spec.rb spec/system/public/articles_spec.rbHeadless by default; visible browser:
SYSTEM_TESTS_BROWSER=1 bundle exec rspec spec/systemReview these host-application settings before deployment:
- Review public template design and enable host Tailwind support for any new utility classes.
- Replace favicons and related icon files if needed.
- Update
public/robots.txtwith the production sitemap URL. - Review the static
public/404.htmlandpublic/500.htmlpages. - Configure S3-compatible production uploads and Litestream backups, or replace those integrations.
With Docker Desktop or another Docker installation with Compose, run from the app repository:
docker compose up --buildVisit http://localhost:3000/onboarding. Local data persists between container restarts. See DEVELOPMENT.md for details.
Build a production image with:
docker build -t postnhost-app .The included fly.toml and Dockerfile support Fly.io deployments after you configure the application name, region, credentials, and persistent volume for your installation.
Distributed under the MIT License. See LICENSE.
