Outline runs as an unprivileged user in the image (``nodejs`` since 1.10.0, ``root`` up to 1.6.1) while the datastore is provisioned by ``root``. Without realignment the application cannot write its ``uploads``, ``public`` and ``avatars`` buckets, so every attachment upload fails with "Permission denied writing to ... Check the host machine file system permissions". This was seen on elabore.coop after the 1.6.1 to 1.10.0 upgrade. The ``init`` hook now reads the image's ``Config.User`` and chowns the datastore to that user, skipping root-based images. It is idempotent and version-agnostic: the realignment also covers buckets added by future Outline versions.
5.0 KiB
Usage
Config info: https://github.com/outline/outline/blob/main/.env.sample
Odoo config: if you configure odoo OIDC connector, the callback url should be like this : https://<YOUR_OUTLINE>:443/auth/oidc.callback
#Requires a smtp-server provider to be functional, you can use
#=smtp-stub= charm to provide information to externally managed SMTP.
outline:
options:
sender-email: #the sender email (beware the conf of your SMTP server)
oidc-client-id: #the client id of your OIDC provider
oidc-client-secret: #the client
oidc-auth-uri: #the host of your OIDC provider
oidc-token-uri: #the token uri of your OIDC provider
oidc-user-info-uri: #the user info uri of your OIDC provider
oidc-logout-uri: #the login uri of your OIDC provider
#smtp-stub:
# options:
# host: smtp.myhost.com
# port: 465
# connection-security: "ssl/tls"
# auth-method: password #IMPORTANT: if not present login password doesn’t work
# login: myuser
# password: myp4ssw0rd
Odoo 14
We monkey-patch odoo in order to make it work, be sure to use latest version in 14.0 of galicea openIDConnection module
Database ownership alignment
The pre_deploy hook ensures that every object of the database
(tables, sequences, views, materialized views, standalone types,
functions, procedures) is owned by the application role before the
container starts and runs its migrations.
Historical provisioning or restores executed as the postgres
superuser leave objects owned by postgres, which makes any later
ALTER on these objects fail with "must be owner of …" and puts
outline in a crash-loop at migration time. This was seen on
2026-09-11 when upgrading elabore.coop from 1.6.1 to 1.10.0:
migration 20260714000000-add-mcp-to-search-queries-source.js
failed on enum_search_queries_source. The same drift was found
on every managed server (lokavaluto.fr, lagemme.org, moneko.org).
Extensions are excluded from the realignment (they are managed by
the postgres charm). The hook is idempotent and silent when
there is no drift, and blocks the deployment (exit 1) if the
realignment fails, so the problem is visible at deploy time instead
of as a cryptic crash-loop.
Datastore ownership alignment
The init hook aligns the ownership of the service datastore with
the user the Outline container runs as. Since 1.10.0 the image runs
as the unprivileged nodejs user (older images, up to 1.6.1, ran as
root), while the datastore is provisioned by root. Without
realignment the application cannot write its uploads, public and
avatars buckets and every attachment upload fails with "Permission
denied writing to … Check the host machine file system
permissions". This was seen on 2026-09-11 on elabore.coop after the
1.6.1 to 1.10.0 upgrade, on every existing datastore.
The hook reads the user from the image's Config.User, so it stays
version-agnostic: images running as root are left untouched, and
re-running the hook on an already aligned datastore is a no-op.
Building a new image
We use the official image with an added patch due to 2 bugs:
- https://github.com/outline/outline/issues/6859
- second was not reported yet
Note that a PR was pushed with a fix on the first bug. But this was not yet tested.
The fix are on 1.6.1
Fix
Upon calling "/oidc" url, outline will return "Set-Cookie" header with a "domain:" value that is incorrect (still the inner docker domain: "outline" instead of the outer proxy domain from the frontend.)
Fortunately we can simply remove the value "domain" from the cookie by
commenting only 2 lines in build/server/utils/passport.js.
The patches will change the "build/" files, so this is a very temporary and brittle fix.
IMAGE=docker.0k.io/outline:1.6.1-elabore
echo 'apt update && apt install patch' | dupd -u "$IMAGE" -- -u 0
cat <<'EOF1' | dupd -u "$IMAGE" -- -u 0
patch -p 1 <<'EOF2'
--- a/build/server/utils/passport.js.orig
+++ b/build/server/utils/passport.js
@@ -56,7 +56,7 @@
const state = buildState(host, token, client);
ctx.cookies.set(this.key, state, {
expires: (0, _dateFns.addMinutes)(new Date(), 10),
- domain: (0, _domains.getCookieDomain)(ctx.hostname, _env.default.isCloudHosted)
+ //domain: (0, _domains.getCookieDomain)(ctx.hostname, _env.default.isCloudHosted)
});
callback(null, token);
});
@@ -73,7 +73,7 @@
// Destroy the one-time pad token and ensure it matches
ctx.cookies.set(this.key, "", {
expires: (0, _dateFns.subMinutes)(new Date(), 1),
- domain: (0, _domains.getCookieDomain)(ctx.hostname, _env.default.isCloudHosted)
+ //domain: (0, _domains.getCookieDomain)(ctx.hostname, _env.default.isCloudHosted)
});
if (!token || token !== providedToken) {
return callback((0, _errors.OAuthStateMismatchError)(), false, token);
EOF2
EOF1