Docs / Programming & Development / Ruby Sinatra Application

Ruby Sinatra Application

By Admin · Mar 15, 2026 · Updated Apr 24, 2026 · 325 views · 3 min read

Sinatra is a lightweight Ruby DSL for creating web applications with minimal effort. Unlike Rails, Sinatra gives you just what you need for web routing and leaves everything else to your choice. This guide covers building and deploying a Sinatra application on a VPS with Puma and Nginx.

Project Setup

# Install Ruby via rbenv
sudo apt install rbenv ruby-build
rbenv install 3.3.0
rbenv global 3.3.0

# Create project
mkdir sinatra-app && cd sinatra-app

# Gemfile
source "https://rubygems.org"
gem "sinatra", "~> 4.0"
gem "sinatra-contrib"
gem "puma", "~> 6.4"
gem "sequel", "~> 5.77"
gem "pg"
gem "json"
gem "rack", "~> 3.0"

# Install
bundle install

Application Code

# app.rb
require "sinatra/base"
require "sinatra/json"
require "sequel"
require "json"

DB = Sequel.connect(ENV["DATABASE_URL"] || "postgres://localhost/sinatra_dev")

class App < Sinatra::Base
  configure do
    set :server, :puma
    set :bind, "0.0.0.0"
    set :port, ENV.fetch("PORT", 3000)
    set :show_exceptions, false
    set :logging, true
  end

  before do
    content_type :json
  end

  get "/health" do
    json status: "healthy"
  end

  get "/api/users" do
    users = DB[:users].all
    json users
  end

  get "/api/users/:id" do
    user = DB[:users].where(id: params[:id].to_i).first
    halt 404, json(error: "Not found") unless user
    json user
  end

  post "/api/users" do
    data = JSON.parse(request.body.read)
    id = DB[:users].insert(name: data["name"], email: data["email"])
    user = DB[:users].where(id: id).first
    status 201
    json user
  end

  error do
    status 500
    json error: "Internal server error"
  end
end

Puma Configuration

# config/puma.rb
workers ENV.fetch("WEB_CONCURRENCY", 2)
threads_count = ENV.fetch("MAX_THREADS", 5)
threads threads_count, threads_count

port ENV.fetch("PORT", 3000)
environment ENV.fetch("RACK_ENV", "production")

preload_app!

on_worker_boot do
  Sequel.connect(ENV["DATABASE_URL"])
end

Rack Configuration

# config.ru
require "./app"
run App

Deployment

# On VPS
cd /opt/sinatra-app
bundle install --deployment --without development test

# Systemd service
# /etc/systemd/system/sinatra-app.service
[Unit]
Description=Sinatra Application
After=network.target

[Service]
Type=simple
User=appuser
WorkingDirectory=/opt/sinatra-app
ExecStart=/usr/local/bin/bundle exec puma -C config/puma.rb
EnvironmentFile=/opt/sinatra-app/.env
Restart=always

[Install]
WantedBy=multi-user.target

Docker Deployment

FROM ruby:3.3-alpine
RUN apk add --no-cache build-base postgresql-dev
WORKDIR /app
COPY Gemfile Gemfile.lock ./
RUN bundle install --jobs 4 --retry 3 --without development test
COPY . .
USER nobody
EXPOSE 3000
CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]

Summary

Sinatra is perfect for microservices, APIs, and small web applications where Rails would be overkill. With Puma for multi-threaded request handling and Sequel for database access, you get a production-ready Ruby stack in a fraction of Rails complexity. The deployment is straightforward: bundle install, configure Puma, run behind Nginx, and manage with systemd.

Was this article helpful?