---
title: "Deploy BlogFactory | (Just Updated) AI Blog Content Engine with MCP, Postgres, Data Survives Redeploys"
description: "BlogFactory AI blog content engine with MCP, Postgres and S3 storage"
category: "AI/ML"
url: https://railway.com/deploy/blogfactory-or-just-updated-ai-blog-cont
---

# Deploy BlogFactory | (Just Updated) AI Blog Content Engine with MCP, Postgres, Data Survives Redeploys

BlogFactory AI blog content engine with MCP, Postgres and S3 storage

**[Deploy BlogFactory | (Just Updated) AI Blog Content Engine with MCP, Postgres, Data Survives Redeploys on Railway](https://railway.com/template/blogfactory-or-just-updated-ai-blog-cont)**

Machine-readable deploy manifest (JSON, validated by TemplateCI): https://railway.com/deploy/blogfactory-or-just-updated-ai-blog-cont/manifest.json

- **Creator:** SuperSlowSloth
- **Category:** AI/ML

## Template content

### scheduler

- **Image:** ghcr.io/bon5co/blogfactory-api:0.1.0@sha256:711358cf7287f4343a75d46dc4a82d83f935b03db216f3606d528dc67b594bd2
- **Start command:** `/bin/sh -c 'sleep 120; while true; do bun run src/run-cron-once.ts || true; sleep 21600; done'`

### postgres

- **Image:** postgres:17.10-alpine

### minio

- **Image:** pgsty/minio@sha256:b6bfe7239bfc83fb90d31612d9704d86039dd714f7904b3f1ad68f211e602372
- **Start command:** `/bin/sh -c 'exec /usr/bin/minio server /data --address :9000 --console-address :9001'`

### web

- **Image:** ghcr.io/bon5co/blogfactory-web:0.1.0@sha256:634673da1e1ed2519c198787bceae91181dd3da540195f3f2aed99a533ca7e7e
- **Health check:** /
- **Public domain:** Yes

### api

- **Image:** ghcr.io/bon5co/blogfactory-api:0.1.0@sha256:711358cf7287f4343a75d46dc4a82d83f935b03db216f3606d528dc67b594bd2
- **Health check:** /api/ready

## Documentation

# Deploy and Host BlogFactory on Railway

**Built for an LLM agent to deploy and use.** There is no setup form and nothing to fill in: every secret is generated at deploy time, the admin account is created by the first sign-up with a generated address, and the contract below lists the addresses, variable names and one script that signs up, creates an MCP token and calls the 22-tool MCP endpoint.

BlogFactory is an open-source AI blog content engine. You connect a site, give it an OpenRouter key, and it researches, drafts and queues articles for that site's voice and sitemap. Agents and editors work with it through a web app, a REST API and a Model Context Protocol (MCP) server at `/mcp` that can read content and write drafts but cannot read credentials, publish live or delete content.

## About Hosting BlogFactory

This template runs five services, all pinned by digest to BlogFactory 0.1.0. `web` is an nginx server with the web app and the only public service; it forwards `/api` and `/mcp` to the API over Railway's private network, so the browser talks to one address. `api` is the Bun and Hono API server; on every start it creates the storage bucket, applies the database migrations and then serves. `postgres` is PostgreSQL 17 on a volume. `minio` is an S3-compatible object store (the community `pgsty/minio` build, AGPL-3.0) on a volume, holding article images and uploads. `scheduler` runs the API's drain job every six hours, which publishes queued articles and generates scheduled content.

Sign-up is open on a fresh instance, but a new account is only approved if its address is listed in `ADMIN_EMAILS`. The template generates that address (`admin-` plus 12 random hex characters, then `@blogfactory.invalid`), so a stranger who finds the URL cannot claim the admin account; read it from the `api` service variables, sign up with it, and the account is created as an approved admin. Other sign-ups wait for approval. To generate articles you add your own OpenRouter key in the app under Control, Article Settings, Keys. To use your own admin address, change `ADMIN_EMAILS` on `api` and redeploy.

BlogFactory is released under the GNU AGPL-3.0. Running it for yourself or your organisation is fine; if you offer a modified version to others over a network you must publish your changes. The [wrapper images](https://github.com/bon5co/blogfactory-railway) only bake in configuration and a start script.

### Agent contract

- Public URL: `WEB_APP_URL` on the `api` service (`https://` plus the `web` service's public domain).
- Private URL: `http://api.railway.internal:8080` (API, MCP) and `http://web.railway.internal:8080` (web).
- Credentials: `ADMIN_EMAILS` (the admin address) on `api`. You choose the password at sign-up. `JWT_SECRET`, `API_KEY_ENCRYPTION_SECRET`, `CRON_SECRET`, `POSTGRES_PASSWORD`, `MINIO_ROOT_PASSWORD` are generated per deploy and stay server-side.
- Auth: `POST /api/auth/signup` with `{"email", "password", "consent": true}` returns `{"token"}`; `POST /api/auth/login` returns one for an existing account. Send it as `Authorization: Bearer TOKEN` on `/api/*`. For MCP, `POST /api/mcp/tokens` with `{"name", "scopes": ["content:read", "drafts:write"], "site_ids": [...]}` returns `{"secret": "bf_mcp_..."}`; send it as `Authorization: Bearer SECRET` to `/mcp`.
- Health: `GET /api/health` returns 200 (liveness) and `GET /api/ready` returns 200 only when Postgres and the storage bucket are reachable, both on the public URL; unauthenticated `GET /mcp` returns 401.

```bash
# run with WEB_APP_URL and ADMIN_EMAILS copied from the api service variables; needs curl and jq
U="$WEB_APP_URL"; PW="agent-$(date +%s)-pw"
curl -s -w " health %{http_code}\n" "$U/api/health"
curl -s -w " ready %{http_code}\n" "$U/api/ready"
curl -s -o /dev/null -w "mcp anonymous: %{http_code}\n" "$U/mcp"
T="$(curl -s -H 'Content-Type: application/json' -d "{\"email\":\"$ADMIN_EMAILS\",\"password\":\"$PW\",\"consent\":true}" "$U/api/auth/signup" | jq -r .token)"
curl -s -H "Authorization: Bearer $T" "$U/api/auth/me" | jq -r '.role'
SID="$(curl -s -H "Authorization: Bearer $T" -H 'Content-Type: application/json' -d '{"url":"https://example.com","name":"Demo"}' "$U/api/sites" | jq -r .site.id)"
M="$(curl -s -H "Authorization: Bearer $T" -H 'Content-Type: application/json' -d "{\"name\":\"agent\",\"scopes\":[\"content:read\",\"drafts:write\"],\"site_ids\":[\"$SID\"]}" "$U/api/mcp/tokens" | jq -r .secret)"
curl -s -H "Authorization: Bearer $M" -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' "$U/mcp" | jq -r '.result.tools | length'
```

A fresh deploy prints `{"status":"ok","version":"0.1.0"} health 200`, `{"status":"ready"} ready 200`, `mcp anonymous: 401`, `admin`, and `22`.

## Why Deploy BlogFactory on Railway?

Railway is a singular platform to deploy your infrastructure stack. Railway will host your infrastructure so you don't have to deal with configuration, while allowing you to vertically and horizontally scale it.

By deploying BlogFactory on Railway, you are one step closer to supporting a complete full-stack application with minimal burden. Host your servers, databases, AI agents, and more on Railway.

- **Agent-ready** - generated secrets, a contract block and one working script; no setup form.
- **Closed to strangers by default** - the admin address is generated, so nobody else can claim the admin account on a public URL.
- **Content queue keeps draining** - a scheduler service runs the six-hourly job; the project has no cron service to add by hand.
- **State survives redeploys** - Postgres and the object store each sit on a volume, and secrets stay the same.
- **Pinned by digest** - the images are fixed to BlogFactory 0.1.0, so a redeploy never changes the version under you.

## Common Use Cases

- **Automated SEO blog content** - research, draft and queue articles in your site's voice from its sitemap.
- **A content tool for AI agents** - connect a coding or writing agent to the MCP server to read content and write drafts.
- **Self-hosted alternative to hosted AI blog writers** - keep keys, drafts and uploads on infrastructure you control.
- **Editorial workflow with human approval** - agents write drafts, people publish.

## Dependencies for BlogFactory Hosting

- PostgreSQL 17 with one Railway volume, created by the template.
- An S3-compatible object store (MinIO) with one Railway volume, created by the template.
- Your own OpenRouter API key, added in the app, for article generation.

### Deployment Dependencies

- [BlogFactory repository](https://github.com/BlogFactoryHQ/blogfactory)
- [Self-hosting guide](https://github.com/BlogFactoryHQ/blogfactory/blob/main/docs/self-hosting.md)
- [Wrapper images](https://github.com/bon5co/blogfactory-railway)

### Implementation Details

| Variable | Purpose |
| --- | --- |
| `ADMIN_EMAILS` | Address that becomes the approved admin on sign-up, generated per deploy. |
| `JWT_SECRET`, `API_KEY_ENCRYPTION_SECRET` | Token signing and key encryption secrets, generated per deploy. |
| `CRON_SECRET` | Shared by `api` and `scheduler` for the drain job. |
| `DATABASE_URL` | Connection string to the Postgres service. |
| `S3_ENDPOINT`, `S3_ACCESS_KEY_ID`, `S3_SECRET_ACCESS_KEY` | Private address and generated credentials of the object store. |
| `WEB_APP_URL`, `MCP_ALLOWED_ORIGINS` | HTTPS address of the `web` service. |
| `API_UPSTREAM` | Private address `web` forwards API and MCP requests to. |

The `api` and `web` services read Railway's injected `PORT` (8080).


## Similar templates

- [Chat Chat](https://railway.com/deploy/-WWW5r) — Chat Chat, your own unified chat and search to AI platform.
- [Weaviate](https://railway.com/deploy/weaviate-1) — Vector database with hybrid search and an API key-protected API
- [Möbius v0.1 Personal AI OS](https://railway.com/deploy/mobius-v01-personal-ai-os) — Self-hosted AI agent that builds and runs apps for you, with memory.

Open this page in a browser: https://railway.com/deploy/blogfactory-or-just-updated-ai-blog-cont
