datomic

Ovi Stoica 2025-06-21T09:54:33.537629Z

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 ๐Ÿ‘‡

Ovi Stoica 2025-10-14T07:17:17.604959Z

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!

๐Ÿ”ฅ 5
๐Ÿ™Œ 5
Ovi Stoica 2025-06-21T09:58:52.746189Z

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?

cjohansen 2025-06-21T10:02:05.112229Z

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 ๐Ÿ˜Š

Ovi Stoica 2025-06-21T10:03:23.622279Z

Ah you mean I need transaction.properties

host=shipclojure-datom-transactor
?

cjohansen 2025-06-21T10:04:36.801579Z

Yes

cjohansen 2025-06-21T10:05:14.167699Z

When the transactor starts it writes this host name to storage, which is how the peer finds it.

๐Ÿš€ 1
Ovi Stoica 2025-06-21T10:07:00.404999Z

Awesome! Thank you, Christian!

๐Ÿ˜Š 1
Ovi Stoica 2025-06-21T10:43:18.327529Z

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 ๐Ÿ˜ต

๐Ÿ‘ 7
Ovi Stoica 2025-06-21T10:44:09.669909Z

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

braai engineer 2025-06-21T17:16:13.976159Z

Is datomic.api/query documented for On Prem?

Darlei Soares 2025-06-21T17:24:09.503949Z

Yes, all apis are listed here https://docs.datomic.com/clojure/index.html

๐Ÿ‘ 1