Skip to content

Instantly share code, notes, and snippets.

@AliOsm
Last active April 17, 2024 17:18
Show Gist options
  • Star 20 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AliOsm/5ed876e704f6854f6d839695c995cafd to your computer and use it in GitHub Desktop.
Save AliOsm/5ed876e704f6854f6d839695c995cafd to your computer and use it in GitHub Desktop.
Deploy Rails, GoodJob, PostgreSQL, Redis, Memcached, Meilisearch, and ChromaDB on the same server using Kamal.
KAMAL_REGISTRY_PASSWORD=dckr_pat_xXXxx_x0xXxXx-xX-XXX0xX0x-x
RAILS_MASTER_KEY=00x00xxx000xxx000000xx0x000x0x00
POSTGRES_PASSWORD=xXxxx0xXXx0
MEILI_MASTER_KEY=xXxxx0xXXx0
BLAZER_DATABASE_URL=postgres://service:{POSTGRES_PASSWORD}@service-name-postgres:5432/service_production
# config/initializers/chroma.rb
require 'chroma-db'
Chroma.connect_host = ENV.fetch('CHROMA_DB_URL', 'http://chromadb:9610')
Chroma.logger = Logger.new($stdout)
Chroma.log_level = Chroma::LEVEL_ERROR
# config/database.yml
default: &default
adapter: postgresql
encoding: unicode
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
production:
<<: *default
host: <%= ENV["DB_HOST"] %>
port: 5432
database: service_production
username: service
password: <%= ENV["POSTGRES_PASSWORD"] %>
pool: <%= $PROGRAM_NAME.include?("good_job") ? ENV.fetch("GOOD_JOB_MAX_THREADS", 5).to_i + 3 : ENV.fetch("RAILS_MAX_THREADS", 5).to_i %>
# config/deploy.yml
service: service-name
image: username/image
registry:
username: username
password:
- KAMAL_REGISTRY_PASSWORD
servers:
web:
hosts:
- 127.0.0.1
options:
network: "kamal_network"
labels:
traefik.http.routers.service-name.entrypoints: websecure
traefik.http.routers.service-name.rule: Host(`yourdomain.com`)
traefik.http.routers.service-name.tls: true
traefik.http.routers.service-name.tls.certresolver: letsencrypt
worker:
hosts:
- 127.0.0.1
options:
network: "kamal_network"
cmd: bundle exec good_job start
volumes:
- /root/service-name/storage:/rails/storage
traefik:
options:
network: "kamal_network"
publish:
- "443:443"
volume:
- "/letsencrypt/acme.json:/letsencrypt/acme.json"
args:
entryPoints.web.address: ":80"
entryPoints.websecure.address: ":443"
entryPoints.web.http.redirections.entryPoint.to: websecure
entryPoints.web.http.redirections.entryPoint.scheme: https
entryPoints.web.http.redirections.entrypoint.permanent: true
certificatesResolvers.letsencrypt.acme.email: "youremail@gmail.com"
certificatesResolvers.letsencrypt.acme.storage: "/letsencrypt/acme.json"
certificatesResolvers.letsencrypt.acme.httpchallenge: true
certificatesResolvers.letsencrypt.acme.httpchallenge.entrypoint: web
# Remember to run `kamal env push` after making changes!
env:
clear:
DB_HOST: service-name-postgres
REDIS_URL: redis://service-name-redis:6379
MEMCACHE_SERVERS: service-name-memcached:11211
MEILISEARCH_URL: http://service-name-meilisearch:7700
CHROMA_DB_URL: http://service-name-chromadb:9610
secret:
- RAILS_MASTER_KEY
- POSTGRES_PASSWORD
- MEILI_MASTER_KEY
- BLAZER_DATABASE_URL
accessories:
postgres:
image: postgres:16
roles:
- web
options:
network: "kamal_network"
env:
clear:
POSTGRES_DB: service_production
POSTGRES_USER: service
secret:
- POSTGRES_PASSWORD
cmd: [
postgres,
-c, "pg_stat_statements.track=all",
-c, "shared_preload_libraries=pg_stat_statements"
]
files:
- db/production.sql:/docker-entrypoint-initdb.d/setup.sql
directories:
- data:/var/lib/postgresql/data
redis:
image: redis:7.2.3
roles:
- web
options:
network: "kamal_network"
directories:
- data:/data
memcached:
image: memcached:1.6.22
roles:
- web
options:
network: "kamal_network"
env:
clear:
MEMCACHED_MEMORY_LIMIT: 2048
directories:
- data:/var/lib/memcached/data
meilisearch:
image: getmeili/meilisearch:v1.5
roles:
- web
options:
network: "kamal_network"
env:
clear:
--env: production
--no-analytics: true
--http-addr: localhost:7700
--max-indexing-memory: 10Gb
secret:
- MEILI_MASTER_KEY
directories:
- data.ms:/meili_data/data.ms
- dumps:/meili_data/dumps
- snapshots:/meili_data/snapshots
chromadb:
image: chromadb/chroma:0.4.18.dev24
roles:
- web
options:
network: "kamal_network"
env:
clear:
IS_PERSISTENT: 'TRUE'
cmd: uvicorn chromadb.app:app --workers 1 --host 0.0.0.0 --port 9610 --log-config chromadb/log_config.yml
directories:
- data:/chroma/chroma
healthcheck:
log_lines: 10_000
max_attempts: 10
asset_path: /rails/public/assets
# config/initializers/meilisearch.rb.rb
MeiliSearch::Rails.configuration = {
meilisearch_url: ENV.fetch('MEILISEARCH_URL', 'http://meilisearch:7700'),
meilisearch_api_key: ENV.fetch('MEILI_MASTER_KEY', nil),
timeout: 5,
max_retries: 2
}
# .kamal/hooks/pre-connect
#!/usr/bin/env bash
# Define the SSH connection details
REMOTE_USER="root"
REMOTE_HOST="${KAMAL_HOSTS}"
REMOTE_SSH_PORT="22" # Default SSH port is 22, change if different
# Run the commands remotely via SSH
ssh -p "$REMOTE_SSH_PORT" "$REMOTE_USER@$REMOTE_HOST" 2>&1 > /dev/null <<ENDSSH
docker network inspect kamal_network >/dev/null 2>&1 || docker network create kamal_network
test -d /letsencrypt || mkdir -p /letsencrypt && touch /letsencrypt/acme.json && chmod 600 /letsencrypt/acme.json
test -d service-name/storage || mkdir -p service-name/storage
if ! getent group ubuntu >/dev/null; then
sudo groupadd ubuntu
sudo useradd -m -g ubuntu -s /bin/bash ubuntu
sudo chown -R ubuntu:ubuntu baheth
fi
ENDSSH
# config/environments/production.rb
require 'active_support/core_ext/integer/time'
Rails.application.configure do
# .....
config.cache_store = :mem_cache_store
config.active_job.queue_adapter = :good_job
# .....
end
-- db/production.sql
CREATE DATABASE service_production;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment