---
title: "Deploy Stringer"
description: "Self-hosted RSS reader with no social feed or algorithm"
category: "Other"
url: https://railway.com/deploy/stringer
---

# Deploy Stringer

Self-hosted RSS reader with no social feed or algorithm

**[Deploy Stringer on Railway](https://railway.com/template/stringer)**

Machine-readable deploy manifest (JSON, validated by TemplateCI): https://railway.com/deploy/stringer/manifest.json

- **Creator:** A3A
- **Category:** Other
- **Total deploys:** 1

## Template content

### stringer-worker https://cdn.jsdelivr.net/gh/stringer-rss/stringer@main/public/img/apple-touch-icon-precomposed.png

- **Source:** https://github.com/gridalpha/stringer-railway
- **Start command:** `/app/bin/railway/worker`
- **Health check:** /status/connected

### stringer https://cdn.jsdelivr.net/gh/stringer-rss/stringer@main/public/img/apple-touch-icon-precomposed.png

- **Source:** https://github.com/gridalpha/stringer-railway
- **Start command:** `/app/bin/railway/web`
- **Health check:** /up
- **Public domain:** Yes

### stringer-scheduler https://cdn.jsdelivr.net/gh/stringer-rss/stringer@main/public/img/apple-touch-icon-precomposed.png

- **Source:** https://github.com/gridalpha/stringer-railway
- **Start command:** `/app/bin/railway/scheduler`
- **Health check:** /status/connected

### Postgres https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/postgresql.svg

- **Image:** ghcr.io/railwayapp-templates/postgres-ssl:18

## Documentation

# Deploy and Host Stringer on Railway

Stringer is a self-hosted, anti-social RSS reader: one account, the feeds you chose, nothing else. No recommendation engine, no social graph, no "stories you might like" — just a fast, keyboard-driven inbox for the open web. Self-host Stringer and your subscription list, read state and starred articles live in your own PostgreSQL database rather than somebody's analytics pipeline.

Deploy Stringer on Railway and you get the production shape rather than a single box. The `stringer` service runs the Rails application and is the only one with a public URL. `stringer-worker` fetches and parses feeds in the background, so a slow site never blocks a page load. `stringer-scheduler` runs the cron that refreshes every five minutes and prunes old read articles nightly. All three use a managed `Postgres` service holding every feed, story and preference. The admin account is created before the app accepts its first request, so the sign-up form is never open to a stranger who finds the URL first.

![Stringer web, worker and scheduler services around Railway Postgres](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789620258/stringer-architecture.webp)

## Getting Started with Stringer on Railway

Open the `stringer` service's public URL and you land on a login form. Sign in with the username and password you set in `STRINGER_USERNAME` and `STRINGER_PASSWORD` at deploy time — that account is created during the first boot and marked admin, and `/setup/password` closes the moment it exists. There are no default credentials to change afterwards.

Your first real action is adding a feed. Click the orange **+** button and paste a site URL — Stringer discovers the feed from an ordinary homepage. The subscription saves immediately and the fetch goes to the worker, so you return to your unread list at once and articles appear within seconds. Or use **Import** in the footer to upload an OPML file exported from Feedly, Inoreader or NewsBlur.

To confirm everything is wired up, open **Feeds** after a minute: each subscription should show a green dot and an unread count. A red dot means that one site failed to respond, not that the deployment is broken. Press `?` for the shortcuts — `j`/`k` move between stories, `Enter` opens one in place, `s` stars it. Stringer also serves the Fever API at `/fever`, so Reeder, Unread and other Fever clients sync against it.

![Stringer unread list mixing Hacker News, The Verge and GitHub stories](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789620261/stringer-story-list.webp)
![A Hacker News story expanded in place above the unread list](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789620263/stringer-story-reading.webp)
![Three subscribed feeds with green status and unread counts](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789620264/stringer-feeds.webp)
![GoodJob dashboard showing the scheduled feed fetch succeeded](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789620265/stringer-good-job.webp)

## About Hosting Stringer

Stringer is a Rails 8 application on PostgreSQL, Hotwire and GoodJob. It is deliberately small — an unread list, a reading pane, a starred archive, a feed manager — which is why it self-hosts well: no search cluster, no cache tier, no object storage.

- Feed discovery from a plain site URL, plus OPML import and export
- Keyboard-first reading with a shortcut overlay on `?`
- Starred articles kept indefinitely, read articles pruned on your schedule
- A Fever API clone at `/fever` for third-party mobile clients
- Interface translations for German, Spanish, French, Japanese, Dutch and more
- SSRF protection on every outbound fetch, including each redirect

The Railway deployment splits the roles Stringer's own image bundles into one container: `stringer` serves HTTP and owns migrations, `stringer-worker` fetches and parses, `stringer-scheduler` runs the polling and cleanup schedules.

## Why Deploy Stringer on Railway

Railway handles the parts you do not want to run:

- Managed PostgreSQL with backups, no tuning required
- A TLS public URL for the web service; worker and scheduler stay private
- Health checks on all three services, background processes included
- Per-service scaling, so the worker grows without the web tier

## Common Use Cases

- A personal reading inbox replacing Feedly or Inoreader, without handing over your subscription list
- Tracking competitor blogs, release notes and changelogs on a shared account
- A stable Fever API endpoint so phone and laptop share read state

## Dependencies for Stringer

- **stringer** — the Rails web application, built from `gridalpha/stringer-railway` on top of `stringerrss/stringer:latest`
- **stringer-worker** — the same image running GoodJob as a background job processor
- **stringer-scheduler** — the same image running GoodJob's cron
- **Postgres** — managed `ghcr.io/railwayapp-templates/postgres-ssl:18`

### Environment Variables Reference

| Variable | Service | Purpose |
|---|---|---|
| `STRINGER_USERNAME` | stringer | Username for the admin account created on first boot |
| `STRINGER_PASSWORD` | stringer | Password for that account |
| `DATABASE_URL` | all three | Connection string for the managed Postgres service |
| `SECRET_KEY_BASE` | all three | Signs the session cookie; must not change |
| `ENCRYPTION_PRIMARY_KEY` | all three | Encrypts the stored Fever API key; must not change |
| `FETCH_FEEDS_CRON` | stringer-scheduler | How often feeds are polled, default every five minutes |
| `CLEANUP_AFTER_DAYS` | stringer-scheduler | Age at which read, unstarred articles are deleted |
| `GOOD_JOB_MAX_THREADS` | stringer-worker | Concurrent feed fetches, default 5 |

### Deployment Dependencies

- Source: [github.com/stringer-rss/stringer](https://github.com/stringer-rss/stringer) (MIT)
- Image: [hub.docker.com/r/stringerrss/stringer](https://hub.docker.com/r/stringerrss/stringer)
- Runtime: Ruby 4.0, Rails 8.1, PostgreSQL 18

## Hardware Requirements for Self-Hosting Stringer

| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 0.5 vCPU across all services | 1–2 vCPU |
| RAM | 1 GB total | 2 GB total |
| Storage | 1 GB in Postgres | 5 GB for a large archive |
| Runtime | Ruby 4.0, PostgreSQL 14+ | Ruby 4.0, PostgreSQL 18 |

The dominant cost is database rows, which grow with the number of feeds and how long read articles are kept. Lowering `CLEANUP_AFTER_DAYS` keeps it small; starred articles are never pruned.

## Self-Hosting Stringer Outside Railway

Stringer publishes a Docker image, but it ships without its compiled CSS and JavaScript — its build never installs the Node toolchain — so running it yourself means building the assets first. These are shell commands to build and start it from source against a local PostgreSQL:

```
git clone https://github.com/stringer-rss/stringer.git && cd stringer
bundle install
pnpm install && pnpm build && pnpm build:css
export DATABASE_URL="postgres://stringer:secret@localhost/stringer_live"
for k in SECRET_KEY_BASE ENCRYPTION_PRIMARY_KEY \
         ENCRYPTION_DETERMINISTIC_KEY ENCRYPTION_KEY_DERIVATION_SALT; do
  export $k=$(openssl rand -hex 32)
done
bundle exec rails db:migrate assets:precompile
bundle exec puma -C ./config/puma.rb
```

To run the published image instead, start PostgreSQL beside it and pass the same four secrets:

```
docker run -p 8080:8080 \
  -e DATABASE_URL=postgres://postgres:changeme@db/stringer \
  -e SECRET_KEY_BASE=$(openssl rand -hex 64) \
  -e ENCRYPTION_PRIMARY_KEY=$(openssl rand -hex 32) \
  -e ENCRYPTION_DETERMINISTIC_KEY=$(openssl rand -hex 32) \
  -e ENCRYPTION_KEY_DERIVATION_SALT=$(openssl rand -hex 32) \
  stringerrss/stringer:latest
```

The Railway template does all of this for you, asset build included.

## Is Stringer Free to Self-Host?

Stringer is open source under the MIT licence, with no paid tier, no feature gate and no account to register. The only cost on Railway is infrastructure: three small services and a managed PostgreSQL database. For one reader following a few hundred feeds that is a small monthly bill, against roughly $6–$10 a month for Feedly Pro or Inoreader Supporter.

## FAQ

**What is Stringer?**

Stringer is a self-hosted RSS and Atom reader written in Ruby on Rails. It gives one user a fast, keyboard-driven unread list for the sites they subscribe to — no social features, no algorithmic ranking, no tracking.

**What does this Railway template deploy?**

Four services: `stringer` (the web application, with a public URL), `stringer-worker` (fetches feeds in the background), `stringer-scheduler` (the cron triggering refreshes and cleanup) and `Postgres`.

**Why does the template include a PostgreSQL database?**

Stringer stores subscriptions, every fetched article, read and starred state, and your account in PostgreSQL. There is no file-based mode, so the database is the only persistent state — a Railway backup captures the whole reader.

**Why are the worker and scheduler separate services?**

Fetching a feed means an HTTP request to somebody else's server, which can be slow or hang. Running that outside the web process keeps the interface responsive, and lets you raise `GOOD_JOB_MAX_THREADS` on its own.

**How do I use Stringer with a mobile RSS client?**

In any Fever-compatible client — Reeder, Unread, Fiery Feeds — set the server to `https://your-domain/fever`, the username to `stringer` and the password to your own. Read and starred state syncs both ways.

**How often does Stringer check for new articles?**

Every five minutes by default. Change `FETCH_FEEDS_CRON` on `stringer-scheduler` to any cron expression — `0 * * * *` for hourly — and redeploy that service.

**Can more than one person use the same deployment?**

Stringer is built for a single reader, and sign-ups close once the first account exists. A household or team can share one login, but there are no per-user feed lists.


## Similar templates

- [Rocky Linux](https://railway.com/deploy/rocky-linux) — Hosted Rocky Linux 9 workspace with SSH and persistent storage. 🚀
- [Foundry Virtual Tabletop](https://railway.com/deploy/X5tR6G) — A Self-Hosted & Modern Roleplaying Platform
- [Letta Code Remote](https://railway.com/deploy/letta-code-remote) — Run a Letta Code agent 24/7. No inbound ports, just deploy.

Open this page in a browser: https://railway.com/deploy/stringer
