Hello datomic fellows! I need some help trying to debug a deployment scenario using http://kamal-deploy.org (think docker-compose + deployment to remote server support). TLDR: How can I setup a peer using SQL storage to connect to the DB on a server and the transactor on another server? Or simpler: How can I tell the peer about the transactorโs url? More details in the thread ๐
This is long overdue but I finally wrote that blog post ๐ https://clojurians.slack.com/archives/C8NUSGWG6/p1760426101677749 Any feedback to the setup would be appreciated!
Hereโs the deploy.yml - kamalโs configuration
# Name of your application. Used to uniquely configure containers.
service: shipclojure-datom
# Name of the container image to pull from docker
image: stoica94/shipclojure-datom
# Deploy to these servers.
servers:
- hetzner-shipclojure # alias for the ip
# Inject ENV variables into containers (secrets come from .kamal/secrets).
env:
clear:
# Datomic configuration
DATOMIC_DB_URI: "datomic:"
MEMCACHED_URL: "shipclojure-datom-memcached:11211"
accessories
# PostgreSQL database - Used for datomic storage
db:
image: postgres:17.5-bookworm
host: hetzner-shipclojure
# Make port available only to localhost not public to the internet
port: "127.0.0.1:5432:5432"
env:
clear:
POSTGRES_USER: datomic
POSTGRES_PASSWORD: datomic
POSTGRES_DB: datomic
files:
- resources/storage/init.sql:/docker-entrypoint-initdb.d/setup.sql
directories:
- datomic_storage_data:/var/lib/postgresql/data
# Memcached
memcached:
image: memcached:1.6-bookworm
host: hetzner-shipclojure
port: "127.0.0.1:11211:11211" # Only accessible locally
options:
health-cmd: "echo 'stats' | nc localhost 11211"
health-interval: 30s
health-timeout: 10s
health-retries: 3
# Datomic Transactor
transactor:
image: stoica94/datomic-pro-clj-1-12-bookworm-slim:1.0.7364 # prebuilt version of the image from resources/images/datomic/Dockerfile
host: hetzner-shipclojure
port: "127.0.0.1:4334:4334" # Only accessible locally for peer connections
cmd: "/opt/datomic/bin/transactor /opt/datomic/config/transactor.properties"
files:
- resources/storage/sql-transactor.properties:/opt/datomic/config/transactor.properties
directories:
- datomic_transactor_logs:/opt/datomic/logs
transactor.properties
###################################################################
protocol=sql
host=localhost
port=4334
###################################################################
# kamal let's you use <service>-<accessory> to describe the host of that
accessory
sql-url=jdbc:
sql-user=datomic
sql-password=datomic
## The Postgres driver is included with Datomic. For other SQL
## databases, you will need to install the driver on the
## transactor classpath, by copying the file into lib/,
## and place the driver on your peer's classpath.
sql-driver-class=org.postgresql.Driver
## Recommended settings for -Xmx1g usage, e.g. dev laptops.
memory-index-threshold=32m
memory-index-max=256m
object-cache-max=128m
The peer code in my own app
;; uri is mapped to the env variable DATOMIC_DB_URI
(defn create-datomic-db [uri]
(let [halt? (atom false)]
(loop []
(try
(let [created? (d/create-database uri)]
(if created?
(t/log! :info "Datomic database created successfully")
(t/log! :info "Datomic database already exists")))
(reset! halt? true)
(catch Exception e
(t/log! :warn ["Failed to create datomic database, retrying in 5 seconds. Error: " (.getMessage e)])
(Thread/sleep 5000)))
(when-not @halt?
(recur)))))
(defmethod ig/init-key :db.datomic/connection
[_ {:keys [db-uri schema-file]}]
(t/log! {:level :info} "Creating datomic connection")
(create-datomic-db db-uri)
(let [conn (d/connect db-uri)]
(transact-all conn (read-txs schema-file))
conn))
What is important is that within Kamal, you have access to the host of an accessory process by saying <service>-<accessory>. The database is shipclojure-datom-db, and to access the PostgreSQL instance with the port, use shipclojure-datom-db:5432. The transactor host is shipclojure-datom-transactor.
THE PROBLEM
The datomic peer lives inside my own app code, and connects to the URI "datomic:", but I get the error.
2025-06-21T09:32:10.095183367Z 2025-06-21T09:32:10.093509363Z WARN LOG hetzner-shipclojure-ef0118fed3aa saas.datomic.core[36,11] Failed to create datomic database, retrying in 5 seconds. Error: Error communicating with HOST localhost on PORT 4334
2025-06-21T09:32:15.108218045Z 2025-06-21T09:32:15.107553803Z WARN LOG hetzner-shipclojure-ef0118fed3aa saas.datomic.core[36,11] Failed to create datomic database, retrying in 5 seconds. Error: Error communicating with HOST localhost on PORT 4334
This means the peer from my app code, I assume, tries to connect to the transactor on localhost:4334, but that is not the correct URL; it should be shipclojure-datom-transactor:4334.
How can I make the peer aware of the correct transactor host?The peer process connects to storage to retrieve the location of the transactor. That part looks to be working. Storage is telling you that the transactor is at localhost, which is what you specified in your transactor.properties. So you're very close! You need to set the right host name at the top of the properties file ๐
Ah you mean I need transaction.properties
host=shipclojure-datom-transactor
?Yes
When the transactor starts it writes this host name to storage, which is how the peer finds it.
Awesome! Thank you, Christian!
Fixed and deployment ready ๐! Iโll make a blog post about using kamal to deploy a datomic clojure web app. This took me 4 days to figure out ๐ต
Whatโs cool about kamal is that once you figure out the deployment for one destination, deploying to any other VM is just having ssh access to it and knowing the host/ip
Is datomic.api/query documented for On Prem?
Yes, all apis are listed here https://docs.datomic.com/clojure/index.html