Yeeted

HOSTING FOR AI-BUILT APPS

Deploy a Rails blog API with PostgreSQL on Yeeted

A Yeeted sample: Rails 8 API-only on managed postgres. The tree is what rails new gives you:

rails new blog-rails --api --database=postgresql \
  --skip-solid --skip-docker --skip-kamal --skip-ci --skip-git \
  --skip-jbuilder --skip-brakeman --skip-rubocop --skip-test

Plus one model, two controllers, a migration, one pinned gem (below) and the three files every example here carries (yeeted.yaml, smoke.sh, this README). Configuration largely follows the generated project. That is the point: it deploys close to as generated.

What it demonstrates

The one edit rails new needed

gem "json", "~> 2.7"

json 3.0 dropped the positional options argument from JSON.parse, and ActiveSupport 8.1 still calls it that way:

activesupport-8.1.3.1/lib/active_support/json/decoding.rb:25
  data = ::JSON.parse(json, options)

so every JSON request body raises ArgumentError: wrong number of arguments (given 2, expected 1), which Rails reports as ActionDispatch::Http::Parameters::ParseError and a 400 with an empty body. Form-encoded bodies are unaffected, which is what makes it look like the client is at fault rather than the stack.

This is not platform-specific: The dependency combination described above resolves json 3.x and behaves the same anywhere. Pinning to 2.x is the fix until ActiveSupport catches up.

Two things worth copying

Give an API app a root route. The root JSON index gives visitors a useful starting point. This example declares /healthz as its health-check path.

Rescue ParameterMissing. Production Rails turns an unhandled one into a 400 with an empty body, which reads as the platform rejecting the request rather than the app. PostsController rescues it into a JSON message.

Endpoints

Route What it is
GET / a JSON index: app name, Rails version, endpoints
GET /healthz ok
GET /api/posts the 50 newest posts
POST /api/posts {"post":{"title":"…","body":"…"}}, 201 or 400

Known cosmetic noise

The generated Gemfile carries image_processing, so every boot logs Using vips to process variants requires the libvips library. This app has no ActiveStorage variants; the gem is left in because the tree is meant to be what rails new produces, and the warning is harmless.

No asset pipeline

--api means there is no assets:precompile task at all, and the build says so rather than failing:

yeeted: no assets:precompile task (API-only, or no asset pipeline) - skipping

A full-stack Rails app is a different story. jsbundling-rails and cssbundling-rails need node and yarn, which ruby:3.3-slim does not carry, and that precompile FAILS the build by design. It used to be swallowed, and the image shipped and 500'd on its first asset.

Tests: ./smoke.sh https://... (smoke). --skip-test was passed: Rails model tests want a database, and every example here runs its unit tests with no services at all.

Run locally

Use Ruby 3.3 or newer, Bundler, and PostgreSQL with a disposable database. From ruby/blog-rails inside the examples tree, set DATABASE_URL to that local database using your environment, then run:

bundle install
bin/rails db:migrate
bin/rails server -b 0.0.0.0 -p 3000

Open http://localhost:3000 for the JSON index. This example has no in-memory or SQLite fallback and no standalone unit tests. The checked-in Gemfile and lockfile are the source of truth for the dependency versions described above.

Deploy and verify

Follow the deployment walkthrough. Upload this folder, including its lockfile, migrations, and yeeted.yaml. The manifest requests PostgreSQL; the platform supplies DATABASE_URL and runs Rails migrations during the release step. This is an API-only app, so there is no asset compilation step.

sh smoke.sh "$PREVIEW_URL"
curl -i "$PREVIEW_URL/api/posts" -H 'Content-Type: application/json' \
  -d '{"post":{"title":"My first Yeeted post","body":"Hello from Rails"}}'
curl -fsS "$PREVIEW_URL/api/posts"

A valid write returns 201. The list response contains the post and "store":"postgres". A blank title returns 400. The smoke script checks health, the root index, creation, reading, and validation. Wrap request fields in post; this is different from the Next.js example's JSON shape.

What to change before real use

The API has no author authentication or per-user ownership. Add those before collecting real users' content. Keep a database backup before experimenting with schema changes; rolling back an application version does not reverse Rails migrations. A full-stack Rails application with JavaScript or CSS bundling has different build requirements from this API-only example.

All examples · Deploy an example