Deploy to Railway

Deploy self-hosted Quackback to Railway with the one-click template (app, PostgreSQL with pgvector, and a storage bucket) or set it up manually. No Redis needed.

JM
James Morton
Written By James MortonLast updated 1 day ago

Deploy self-hosted Quackback to Railway with one click: no server to manage, automatic SSL, and PostgreSQL plus file storage included. Railway is a third-party hosting platform you pay for directly; this is separate from Quackback Cloud.

Prerequisites

  • A Railway account
  • A domain or subdomain for your instance (for example feedback.yourcompany.com), or a Railway-generated domain

One-click deploy

Open the Quackback template on Railway and click Deploy.

The template provisions three services:

Service

Purpose

Quackback

The application server, including background jobs

PostgreSQL

Database with the pgvector extension (pgvector/pgvector image)

Bucket

Railway's S3-compatible storage for file uploads

There is no Redis service: PostgreSQL holds the background job queue too.

The template sets TRUSTED_PROXY_HOPS=1, because Railway's edge proxies every request. If you turn on Railway's CDN in front of the service, change it to 2.

After deploying:

  1. Find your sign-in code. If email isn't configured yet, sign-in codes appear in the Quackback service's deploy logs.
  2. Configure email (recommended): add one email provider, as shown below.
  3. Add a custom domain (optional), then update BASE_URL to match.

Manual setup

If you prefer to configure each service yourself:

  1. Create a new project. Go to railway.com/new and click Empty Project.
  2. Add PostgreSQL. Click + New > Docker Image and enter pgvector/pgvector:pg17. Add a volume mounted at /var/lib/postgresql/data, and set POSTGRES_DB, POSTGRES_USER, and POSTGRES_PASSWORD.
  3. Add a bucket. Click + New > Bucket. Railway provisions S3-compatible storage for uploads.
  4. Deploy Quackback. Click + New > Docker Image and enter ghcr.io/quackbackio/quackback:latest (or pin a release tag).
  5. Configure environment variables (see below).
  6. Set the health check. In the Quackback service, go to Settings > Deploy and set the health check path to /api/health/ready. The first start after an upgrade runs migrations, so allow a generous timeout (the template uses 300 seconds).
  7. Add a domain. Go to Settings > Networking, click Generate Domain for a Railway subdomain or Custom Domain for your own (add a CNAME record pointing to Railway), then update BASE_URL to match. Railway provides SSL automatically.

Note: Quackback requires PostgreSQL 14+ with pgvector 0.5.0+ and pg_trgm. The pgvector/pgvector image includes both. Connect over Railway's private network directly, not through a transaction-mode pooler: realtime updates use LISTEN/NOTIFY.

Environment variables

In the Quackback service settings, add:

DATABASE_URL=postgresql://quackback:${{postgres.POSTGRES_PASSWORD}}@${{postgres.RAILWAY_PRIVATE_DOMAIN}}:5432/quackback

# Authentication, 32+ characters (generate with: openssl rand -base64 32)
SECRET_KEY=your-32-character-minimum-secret-key

# Public URL (set after assigning a domain)
BASE_URL=https://feedback.yourcompany.com

# Railway's edge is one proxy hop (use 2 if Railway's CDN is enabled)
TRUSTED_PROXY_HOPS=1

# Storage bucket
S3_ENDPOINT=${{Bucket.ENDPOINT}}
S3_BUCKET=${{Bucket.BUCKET}}
S3_REGION=${{Bucket.REGION}}
S3_ACCESS_KEY_ID=${{Bucket.ACCESS_KEY_ID}}
S3_SECRET_ACCESS_KEY=${{Bucket.SECRET_ACCESS_KEY}}
S3_FORCE_PATH_STYLE=true

Do not set REDIS_URL. It is no longer used.

Email configuration

Without email, sign-in codes are printed to the Railway logs. For production, configure exactly one sending provider: SMTP, Amazon SES, or Resend. If more than one is set, the app refuses to start and names the conflicting variables.

# SMTP
EMAIL_SMTP_HOST=smtp.example.com
EMAIL_SMTP_PORT=587
EMAIL_SMTP_USER=your-username
EMAIL_SMTP_PASS=your-password
EMAIL_FROM=Quackback <[email protected]>

# Or Amazon SES (all three keys required)
# EMAIL_SES_ACCESS_KEY_ID=AKIA...
# EMAIL_SES_SECRET_ACCESS_KEY=...
# EMAIL_SES_REGION=us-east-1

# Or Resend
# EMAIL_RESEND_API_KEY=re_xxxxxxxxxxxx

See Configure email delivery for details.

File storage

The template's Railway bucket stores uploads. Files are served through the app at /api/storage, so the bucket can stay private. To use another provider instead (AWS S3, Cloudflare R2, Backblaze B2), point the S3_* variables at it. See Environment variables.

Upgrades

Migrations run on every startup, so schema changes apply when the new image starts. To upgrade:

  1. Back up the PostgreSQL volume and the bucket.
  2. Change the Quackback service's image tag to the new release (or redeploy to pull latest).
  3. Watch the deploy logs until migrations finish and the health check passes.

Warning: Upgrading from 0.13? Follow Upgrade from 0.13 to 0.14. Remove the old Redis service and the REDIS_URL variable, and add TRUSTED_PROXY_HOPS=1.

Monitoring

  • Logs: open the Quackback service, click Deployments, select the active deployment, and click View Logs.
  • Health check: in Settings > Deploy, set the path to /api/health/ready (checks the database, migrations, and background workers). Use /api/health/live if you only want to confirm the process is up. See Health endpoints.

Cost

Railway bills based on usage. A typical Quackback instance costs about $5 to $20 a month depending on traffic:

Resource

Estimate

Quackback (512 MB RAM)

about $5/mo

PostgreSQL

about $5/mo

Bucket

Depends on stored files

Check Railway's pricing page for current plans and trial credit.

Troubleshooting

Deploy fails

Check the deploy logs. Common causes:

  • Missing environment variables: make sure DATABASE_URL, SECRET_KEY, and BASE_URL are set.
  • More than one email provider configured: the log names the conflicting variables.

Database connection failed

  1. Verify the PostgreSQL service is running.
  2. Check that DATABASE_URL references the correct service.
  3. Restart the Quackback service.

Port already in use

Railway assigns a port through the PORT environment variable, and Quackback reads it automatically. Do not hardcode a port.

Was this helpful?

Your feedback shapes what we write next.