SQLite3 - CantOpenException: Kamal, docker, SQLite3 improperly configured?

Any help appreciated.

Using Rails 7.2, Kamal 2, Docker and deploying to Hetzner.

At the moment I am getting an error, when I run kamal deploy:

ActiveRecord::StatementInvalid: SQLite3::CantOpenException: unable to open database file (ActiveRecord::StatementInvalid)

Caused by:
SQLite3::CantOpenException: unable to open database file (SQLite3::CantOpenException)

The last step before the issue I set up DB production

# SQLite. Versions 3.8.0 and up are supported.
#   gem install sqlite3
#
#   Ensure the SQLite 3 gem is defined in your Gemfile
#   gem "sqlite3"
#
default: &default
  adapter: sqlite3
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  timeout: 5000

development:
  <<: *default
  database: storage/development.sqlite3

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  <<: *default
  database: storage/test.sqlite3


# SQLite3 write its data on the local filesystem, as such it requires
# persistent disks. If you are deploying to a managed service, you should
# make sure it provides disk persistence, as many don't.
#
# Similarly, if you deploy your application as a Docker container, you must
# ensure the database is located in a persisted volume.
production:
  <<: *default
  database: /app/storage/production.sqlite3

and set

volumes:
  - "app_storage:/app/storage"

in Dockerfile

# Name of your application. Used to uniquely configure containers.
service: find-a-coach

# Name of the container image.
image: svalasek/find-a-coach

# Deploy to these servers.
servers:
  web:
    - 168.119.124.201
  # job:
  #   hosts:
  #     - 192.168.0.1
  #   cmd: bin/jobs

# Enable SSL auto certification via Let's Encrypt (and allow for multiple apps on one server).
# If using something like Cloudflare, it is recommended to set encryption mode
# in Cloudflare's SSL/TLS setting to "Full" to enable end-to-end encryption.
proxy:
  ssl: true
  host: www.findacoach.eu
  # kamal-proxy connects to your container over port 80, use `app_port` to specify a different port.
  # app_port: 3000

# Credentials for your image host.
registry:
  # Specify the registry server, if you're not using Docker Hub
  # server: registry.digitalocean.com / ghcr.io / ...
  username: svalasek

  # Always use an access token rather than real password (pulled from .kamal/secrets).
  password:
    - KAMAL_REGISTRY_PASSWORD

# Configure builder setup.
builder:
  arch: amd64

# Inject ENV variables into containers (secrets come from .kamal/secrets).
#
env:
#   clear:
#     DB_HOST: 192.168.0.2
  secret:
    - RAILS_MASTER_KEY
  clear:
    RAILS_LOG_LEVEL: debug

# Aliases are triggered with "bin/kamal <alias>". You can overwrite arguments on invocation:
# "bin/kamal logs -r job" will tail logs from the first server in the job section.
#
aliases:
    console: app exec --interactive --reuse "bin/rails console"
    shell: app exec --interactive --reuse "bash"
    logs: app logs -f
#   shell: app exec --interactive --reuse "bash"

# Use a different ssh user than root
#
ssh:
  user: kamal
  keys: [ "~/.ssh/id_ed25519" ]

# Use a persistent storage volume.
#
volumes:
  - "app_storage:/app/storage"

# Bridge fingerprinted assets, like JS and CSS, between versions to avoid
# hitting 404 on in-flight requests. Combines all files from new and old
# version inside the asset_path.
#
# asset_path: /app/public/assets

# Configure rolling deploys by setting a wait time between batches of restarts.
#
# boot:
#   limit: 10 # Can also specify as a percentage of total hosts, such as "25%"
#   wait: 2

# Use accessory services (secrets come from .kamal/secrets).
#
# accessories:
#   db:
#     image: mysql:8.0
#     host: 192.168.0.2
#     port: 3306
#     env:
#       clear:
#         MYSQL_ROOT_HOST: '%'
#       secret:
#         - MYSQL_ROOT_PASSWORD
#     files:
#       - config/mysql/production.cnf:/etc/mysql/my.cnf
#       - db/production.sql:/docker-entrypoint-initdb.d/setup.sql
#     directories:
#       - data:/var/lib/mysql
#   redis:
#     image: redis:7.0
#     host: 192.168.0.2
#     port: 6379
#     directories:
#       - data:/data

deploy.yml

# Name of your application. Used to uniquely configure containers.
service: find-a-coach

# Name of the container image.
image: svalasek/find-a-coach

# Deploy to these servers.
servers:
  web:
    - 168.119.124.201
  # job:
  #   hosts:
  #     - 192.168.0.1
  #   cmd: bin/jobs

# Enable SSL auto certification via Let's Encrypt (and allow for multiple apps on one server).
# If using something like Cloudflare, it is recommended to set encryption mode
# in Cloudflare's SSL/TLS setting to "Full" to enable end-to-end encryption.
proxy:
  ssl: true
  host: www.findacoach.eu
  # kamal-proxy connects to your container over port 80, use `app_port` to specify a different port.
  # app_port: 3000

# Credentials for your image host.
registry:
  # Specify the registry server, if you're not using Docker Hub
  # server: registry.digitalocean.com / ghcr.io / ...
  username: svalasek

  # Always use an access token rather than real password (pulled from .kamal/secrets).
  password:
    - KAMAL_REGISTRY_PASSWORD

# Configure builder setup.
builder:
  arch: amd64

# Inject ENV variables into containers (secrets come from .kamal/secrets).
#
env:
#   clear:
#     DB_HOST: 192.168.0.2
  secret:
    - RAILS_MASTER_KEY
  clear:
    RAILS_LOG_LEVEL: debug

# Aliases are triggered with "bin/kamal <alias>". You can overwrite arguments on invocation:
# "bin/kamal logs -r job" will tail logs from the first server in the job section.
#
aliases:
    console: app exec --interactive --reuse "bin/rails console"
    shell: app exec --interactive --reuse "bash"
    logs: app logs -f
#   shell: app exec --interactive --reuse "bash"

# Use a different ssh user than root
#
ssh:
  user: kamal
  keys: [ "~/.ssh/id_ed25519" ]

# Use a persistent storage volume.
#
volumes:
  - "app_storage:/app/storage"

SOLVED

via creating /storage folder and giving it rails user and group

RUN groupadd --system --gid 1000 rails && \
    useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \
    mkdir -p /storage && \
    chown -R rails:rails db log storage tmp /storage

and setting it correctly in deploy.yml

volumes:
  - "find-a-coach-data:/storage"

and database.yml

production:
  <<: *default
  database: /storage/production.sqlite3

I’ve struggled a lot with this too :frowning:

I wish there was just a reliable way to automatically create the directories with the correct permissions/ownership on the host. Especially as I usually create a “deploy” user on the host which then doesn’t have the 1000 uid and gid and things always get messy…