ColdFront Configuration
ColdFront is an optional tiered-storage add-on for a pgEdge Distributed
Postgres node: recent data stays in native Postgres partitions, older data
archives to Apache Iceberg on S3-compatible storage, and both are readable
and writable through the same SQL. It depends on Lakekeeper, the Iceberg
REST catalog every cold-tier commit goes through, and an S3-compatible
object store behind that catalog. The install_coldfront,
setup_coldfront_db and setup_coldfront roles configure the add-on
itself; install_lakekeeper and setup_lakekeeper build the catalog as a
separate, dedicated host.
ColdFront is optional, so its sample playbooks and inventories are kept
apart from the plain ones. The sample-playbooks/simple-cluster-coldfront
playbook runs all five: a play for the lakekeeper group first, then the
pgedge play, with install_coldfront before setup_postgres,
setup_coldfront_db after it, and setup_coldfront after setup_pgedge.
The sample-playbooks/ultra-ha-coldfront playbook does the same on an
Ultra-HA cluster, in mesh mode, with setup_coldfront_db after
setup_patroni.
The simple-cluster and Ultra-HA end-to-end tests cover ColdFront with a
Lakekeeper host and SeaweedFS standing in for the object store. To run that
variant locally, pass coldfront after the operating system:
./tests/run-test.sh simple-cluster debian12 coldfront
./tests/run-test.sh ultra-ha debian12 coldfront
Add mesh as well to run the simple cluster's ColdFront in mesh mode. The
Ultra-HA test always runs it in mesh mode.
Lakekeeper Parameters
The following parameters locate the Iceberg catalog and are read by both
setup_coldfront, which writes to it, and setup_lakekeeper, which
builds it. Set them once, in a scope both host groups can see.
lakekeeper_host
- Type: String
- Default: (none)
- Description: Address the Lakekeeper catalog is reached at.
lakekeeper_bind_ip
- Type: String
- Default:
{{ lakekeeper_host }} - Description: Address the Lakekeeper service itself binds.
lakekeeper_port
- Type: Integer
- Default:
8181 - Description: Port the Iceberg REST catalog and management API listen on.
lakekeeper_metrics_port
- Type: Integer
- Default:
9000 - Description: Port for Lakekeeper's Prometheus metrics. Must differ from
lakekeeper_portandpg_port, or Lakekeeper exits at startup;init_serverrefuses ports that collide on the Lakekeeper host. On that host,pg_portis the port the catalog's own Postgres instance listens on, at the loopback address only.
lakekeeper_warehouse
- Type: String
- Default:
wh - Description: Name of the Iceberg warehouse ColdFront writes through.
lakekeeper_db
- Type: String
- Default:
lakekeeper - Description: Name of Lakekeeper's own catalog database.
lakekeeper_db_user
- Type: String
- Default:
lakekeeper - Description: Database role that owns the catalog database.
lakekeeper_db_password
- Type: String
- Default:
secret - Description: Password for
lakekeeper_db_user.init_serverrefuses to run while it is still the default, so set it on thelakekeepergroup, from Ansible Vault like the other passwords.
lakekeeper_pg_data
- Type: String
- Default:
{{ pg_home }}/{{ pg_version }}/lakekeeper - Description: Data directory of the catalog's own Postgres instance,
which
setup_lakekeepercreates and runs as thelakekeeper-postgresservice.
lakekeeper_namespace
- Type: String
- Default:
public - Description: Iceberg namespace pre-created in the warehouse. Decoupled
(Iceberg-only) tables need this namespace to exist in its own committed
call, because
coldfront.create_iceberg_table()defers the IcebergCREATE SCHEMAto commit but posts theCREATE TABLEeagerly.
all:
vars:
lakekeeper_host: 192.168.1.20
Object Store Parameters
ColdFront's cold tier writes to any S3-compatible object store — real AWS
S3, MinIO, or a self-hosted store — not specifically any one product.
Both setup_coldfront (the archiver's own connection) and
setup_lakekeeper (the warehouse's storage credential, which Lakekeeper
uses to prove access by writing to the bucket) read these.
coldfront_s3_endpoint
- Type: String
- Default:
"" - Description: Full URL of the object store,
http(s)://host[:port]. Left empty, AWS's own endpoint resolution is used instead of a custom one.init_serverrefuses any other form.
coldfront_s3_endpoint: "http://192.168.1.10:8333"
coldfront_s3_region
- Type: String
- Default:
us-east-1 - Description: Region passed to the object store.
coldfront_s3_bucket
- Type: String
- Default:
iceberg - Description: Bucket the cold tier's Parquet data and Iceberg metadata live in.
coldfront_s3_access_key
- Type: String
- Default:
"" - Description: Access key for the object store. Required:
init_serverrefuses an empty value, as it does for the secret key, the bucket and the region.
coldfront_s3_secret_key
- Type: String
- Default:
"" - Description: Secret key for the object store.
In the following example, the inventory retrieves the credentials from Ansible Vault:
coldfront_s3_access_key: "{{ vault_s3_access_key }}"
coldfront_s3_secret_key: "{{ vault_s3_secret_key }}"
coldfront_s3_path_style
- Type: Boolean
- Default:
true - Description: Whether to use path-style addressing, typical for
self-hosted S3-compatible stores. Set to
falsefor virtual-hosted-style addressing, typical for AWS S3.
ColdFront Parameters
The following parameters control ColdFront on the pgEdge nodes.
setup_postgres (or setup_patroni on an HA cluster) reads them as well
as the ColdFront roles, since ColdFront's settings are among the Postgres
parameters it writes. setup_coldfront_db creates ColdFront's role,
database and extensions.
coldfront_enabled
- Type: Boolean
- Default:
false - Description: Whether the pgEdge nodes run ColdFront. Set it on the
pgedgegroup.setup_postgresthen addspg_duckdbandcoldfronttoshared_preload_libraries, after the collection's own libraries and before any thatpg_parametersnames, and writes ColdFront's settings with the rest of the Postgres configuration. The settings ColdFront depends on are parameters the collection controls, soinit_serverrefusespg_parametersthat sets them; its tuning settings are defaultspg_parametersmay override (see Tuning).init_serveralso refusescoldfront_enabledon an HA cluster withoutcoldfront_mesh, withoutlakekeeper_hostor completecoldfront_s3_*settings, or with apg_versionoutsidecoldfront_supported_pg_versions.install_coldfronthas to run beforesetup_postgres, which restarts Postgres to load the libraries, one node at a time, unlesspg_allow_restartisfalse.setup_coldfront_dbthen createscoldfront_duckdb_role,coldfront_dband thepg_duckdbandcoldfrontextensions. The sample playbooks run the ColdFront roles only wherecoldfront_enabledis set.
coldfront_duckdb_role
- Type: String
- Default:
coldfront_duckdb - Description: NOLOGIN role
pg_duckdbgates DuckDB execution on. Superusers always qualify; members reach the cold tier without superuser viacoldfront.grant_app_access().
coldfront_db
- Type: String
- Default:
coldfront - Description: Name of the additional database ColdFront's tiered
storage lives in. Outside mesh mode it stays separate from the
general-purpose databases the cluster lists in
db_names; mesh mode needs it listed there, which puts it into Spock.
coldfront_supported_pg_versions
- Type: List
- Default:
[16, 17, 18] - Description: Postgres major versions ColdFront packages are published
for.
init_serverrefusescoldfront_enabledwith any otherpg_version, before anything is installed, rather than lettinginstall_coldfrontfail with an opaque package-manager error.
coldfront_mesh
- Type: Boolean
- Default:
false - Description: Whether this node coordinates cold-tier commits with other nodes over Spock, rather than a local advisory lock. See below.
Upstream ColdFront distinguishes a single-node "vanilla" mode from an
N-node Spock "mesh" mode that coordinates cold-tier commits with the
Ricart-Agrawala protocol. coldfront_mesh: false, the default, leaves
coldfront.dblink_self unset, which keeps that protocol off even though
setup_postgres configures Spock and Snowflake on every pgEdge node. Each
node then serializes only its own cold-tier commits with a local advisory
lock, so on a cluster of more than one node each Iceberg table must be
written from one node only.
coldfront_mesh: true turns the protocol on, following upstream's mesh
setup. It needs coldfront_db listed in db_names, so that the database
joins Spock alongside the others, and init_server refuses it otherwise.
The setup_coldfront role page covers what
else it sets up. An HA cluster requires true, and init_server refuses
ColdFront there otherwise.
In the following example, the inventory turns mesh mode on for every pgEdge node:
pgedge:
vars:
coldfront_mesh: true
db_names:
- demo
- coldfront
Tuning
ColdFront's tuning settings are defaults, which pg_parameters may override like any other:
| Parameter | Default | Description |
|---|---|---|
coldfront.iceberg_async_parquet |
on |
ColdFront's asynchronous Parquet writing, which the collection turns on. |
coldfront.peer_alive_window_ms |
5000 |
Mesh mode only. How long, in milliseconds, a peer may go without reporting replication progress before the bakery stops waiting for its acknowledgement and treats it as dead. Upstream's default. |
wal_receiver_status_interval |
1s |
Mesh mode only. How often each node reports replication progress, so an idle peer stays inside the window above. |
Raise coldfront.peer_alive_window_ms on slow or lossy links between
nodes, where a live peer can otherwise be ruled dead. Keep
wal_receiver_status_interval well inside it, or every idle peer looks
dead to the bakery.
pgedge:
vars:
pg_parameters:
coldfront.peer_alive_window_ms: 10000
Object Store
This collection does not deploy an object store. Point the parameters above at an S3-compatible store that is already available.