
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
scheduler
Just deployed
postgres
Just deployed
/var/lib/postgresql
minio
Just deployed
/data
Just deployed
Just deployed
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 only bake in configuration and a start script.
Agent contract
- Public URL:
WEB_APP_URLon theapiservice (https://plus thewebservice's public domain). - Private URL:
http://api.railway.internal:8080(API, MCP) andhttp://web.railway.internal:8080(web). - Credentials:
ADMIN_EMAILS(the admin address) onapi. You choose the password at sign-up.JWT_SECRET,API_KEY_ENCRYPTION_SECRET,CRON_SECRET,POSTGRES_PASSWORD,MINIO_ROOT_PASSWORDare generated per deploy and stay server-side. - Auth:
POST /api/auth/signupwith{"email", "password", "consent": true}returns{"token"};POST /api/auth/loginreturns one for an existing account. Send it asAuthorization: Bearer TOKENon/api/*. For MCP,POST /api/mcp/tokenswith{"name", "scopes": ["content:read", "drafts:write"], "site_ids": [...]}returns{"secret": "bf_mcp_..."}; send it asAuthorization: Bearer SECRETto/mcp. - Health:
GET /api/healthreturns 200 (liveness) andGET /api/readyreturns 200 only when Postgres and the storage bucket are reachable, both on the public URL; unauthenticatedGET /mcpreturns 401.
# 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
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).
Template Content

