Billy Bolton
10/01/2026, 4:46 PMBilly Bolton
10/01/2026, 4:48 PMpgcid=$(docker ps --filter status=running -q \
--filter 'label=com.docker.swarm.service.name=aod_postgres' | head -n1)
if [ -z "$pgcid" ]; then
echo "No running aod_postgres container found on this worker"
else
docker exec "$pgcid" psql -U postgres -d cerbos -X -P pager=off -c "
SELECT schemaname, relname, n_live_tup AS estimated_rows,
pg_size_pretty(pg_total_relation_size(
format('%I.%I', schemaname, relname)::regclass
)) AS total_size
FROM pg_stat_user_tables
WHERE relname IN (
'policy', 'policy_dependency', 'policy_ancestor',
'policy_revision', 'attr_schema_defs'
)
ORDER BY relname, schemaname;"
fi
schemaname | relname | estimated_rows | total_size
------------+-------------------+----------------+------------
cerbos | attr_schema_defs | 0 | 16 kB
cerbos | policy | 60348 | 1262 MB
cerbos | policy_ancestor | 3 | 24 kB
cerbos | policy_dependency | 0 | 8192 bytes
cerbos | policy_revision | 60351 | 1263 MB
(5 rows)Dennis (Cerbos)
CERBOS_RULE_TABLE_GC_PERCENT=0 environment variable.Dennis (Cerbos)
SELECT kind, disabled, count(*),
pg_size_pretty(sum(length(definition))) AS total,
pg_size_pretty(max(length(definition))) AS largest
FROM cerbos.policy GROUP BY 1, 2;Billy Bolton
10/02/2026, 1:25 PMBilly Bolton
10/02/2026, 3:49 PMBilly Bolton
10/02/2026, 5:09 PMcerbos=# SELECT kind, disabled, count(*),
cerbos-# pg_size_pretty(sum(octet_length(definition))::bigint) AS total,
cerbos-# pg_size_pretty(max(octet_length(definition))::bigint) AS largest
cerbos-# FROM cerbos.policy
cerbos-# GROUP BY 1, 2;
kind | disabled | count | total | largest
----------+----------+-------+---------+---------
RESOURCE | f | 261 | 9599 kB | 9522 kB
ROLE | f | 60047 | 6734 MB | 230 kB
(2 rows)Billy Bolton
10/02/2026, 6:16 PMDennis (Cerbos)
CERBOS_RULE_TABLE_GC_PERCENT=0)? If so, did it help at all, even partially? A startup time with it would be really useful.
2. What do you mean by "reloading programmatically"? I understood the problem to be startup against an already-filled database. Do you mean loading the policies through the Admin API or an SDK, or triggering a store reload on a running PDP?Dennis (Cerbos)
request.resource.attr.objectId in ["object_id_value_1016", ...], and Cerbos has to parse and compile all of them at startup. On my laptop, your 230 KB sample policy took about 0.2 s to compile. An average policy is half that size, so it should take about half the time, which means 60k policies take over an hour to compile.
The workarounds only mitigate the slow startup, they don't solve it:
- Starting with CERBOS_RULE_TABLE_GC_PERCENT=0 roughly halves compile time but raises peak memory, so check your container's memory limit first.
- Moving the lists into constants cuts compile time by about 3x.
Even with both changes, I'd expect startup to stay in the tens of minutes.
The root cause is that the policies hold data. Policies work best when they describe rules, while data, such as which objects a user group can access, reside elsewhere and reaches Cerbos with each request. If your application knows a user's allowed values at request time, it can pass them as principal attributes. Then a single role policy can check, for example: request.resource.attr.objectId in request.principal.attr.objectIds
That replaces 60k policies with one or a few, and startup becomes a matter of seconds.
If gathering that data in your application isn't practical, Cerbos Synapse, part of our commercial offering, is built for this. It fetches identity and resource context from your databases, IdPs and APIs at decision time, caches it, and adds it to each request before it reaches the PDP, so your policies stay small and your data stays where it belongs.Billy Bolton
10/05/2026, 1:15 PM1. Did you get a chance to try the workaround (I didn't see much difference but if it only halves it, I'll have to dedicate time to benchmark it properly.)? If so, did it help at all, even partially? A startup time with it would be really useful.CERBOS_RULE_TABLE_GC_PERCENT=0
2. What do you mean by "reloading programmatically"? I understood the problem to be startup against an already-filled database. Do you mean loading the policies through the Admin API or an SDK, or triggering a store reload on a running PDP?Yes I mean through the Go SDK, ie here.
- Moving the lists into constants cuts compile time by about 3x.Can you give me an example of the difference?
Billy Bolton
10/05/2026, 10:44 PMDennis (Cerbos)
Can you give me an example of the difference?With 500 copies of your sample role policy, startup went from 107 s to 37 s with default settings and from 54 s to 21 s with
CERBOS_RULE_TABLE_GC_PERCENT=0.
You can use store reloading, but first adjust engine.policyLoaderTimeout in the config. It's 2s by default.Dennis (Cerbos)
Billy Bolton
10/06/2026, 1:23 PMBilly Bolton
10/06/2026, 1:26 PMYou can use store reloading, but first adjustJust to clarify my understanding of reload though: is it true that it behaves similarly or the same as when Cerbos is starting up? (Forgive me for asking without trying -- juggling a few things that I can't risk waiting a while again to test this myself).in the config. It'sengine.policyLoaderTimeoutby default.2s
Dennis (Cerbos)
Dennis (Cerbos)