Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
6cc53c1
Port Odoo appliance to Trixie
lirazsiri Aug 25, 2026
80961a1
Correct Odoo 19 deployment and acceptance
lirazsiri Aug 26, 2026
a385768
Protect Odoo database initialization credentials
lirazsiri Aug 26, 2026
662e047
Remove unavailable Odoo PDF renderer packages
lirazsiri Aug 26, 2026
121776f
Record blocked Odoo acceptance evidence
lirazsiri Aug 26, 2026
6b6c43f
Harden Odoo 19 runtime and acceptance
lirazsiri Aug 26, 2026
9a09c27
Install pinned wkhtmltopdf renderer
lirazsiri Aug 26, 2026
8d5cbca
Bridge Odoo pypdf dependency on Trixie
lirazsiri Aug 26, 2026
49a887b
Fix Odoo 19 config acceptance imports
lirazsiri Aug 26, 2026
c4e657c
Fix Odoo 19 firstboot config import
lirazsiri Aug 26, 2026
7e562aa
Avoid Odoo namespace shadow at firstboot
lirazsiri Aug 26, 2026
aee4e49
Install Odoo JSON acceptance dependency
lirazsiri Aug 26, 2026
f2a9f6e
Set Odoo admin password through ORM
lirazsiri Aug 26, 2026
fe9e7fd
Retain Odoo authentication diagnostics
lirazsiri Aug 26, 2026
42d8365
Fix Odoo service home environment
lirazsiri Aug 26, 2026
031e468
Document final Odoo v19 blocker evidence
lirazsiri Aug 27, 2026
84b004f
Restore Odoo's Trixie PDF dependency alternative
lirazsiri Aug 27, 2026
994fd1a
Keep updater download chatter out of metadata
lirazsiri Aug 27, 2026
a501a82
Activate Odoo commented secret settings
lirazsiri Aug 27, 2026
d3c3932
Limit commented secret activation to Odoo default
lirazsiri Aug 27, 2026
fd511ae
Import Odoo tools in runtime probes
lirazsiri Aug 27, 2026
9ea9d68
docs(turnkey-v19-appliance-migration): distinguish blocker from SHIP
lirazsiri Aug 27, 2026
9bfc97e
fix(odoo-v19-trixie): persist Odoo-owned data directory
lirazsiri Aug 27, 2026
747bdf3
docs(odoo-v19-trixie): record epoch-two validation
lirazsiri Aug 27, 2026
0d5484f
Merge tkldev Odoo migration work
lirazsiri Aug 27, 2026
213ee41
Restore the exact-tested Odoo release tree
lirazsiri Aug 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
CREDIT_ANCHORTEXT = Turnkey Odoo Appliance

BACKPORTS=y # install Odoo v16.x from backports
BACKPORTS_PINS=odoo-16

include $(FAB_PATH)/common/mk/turnkey/lapp.mk
include $(FAB_PATH)/common/mk/turnkey.mk
17 changes: 10 additions & 7 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,29 @@ or paid commercial ones.

This appliance includes all the standard features in `TurnKey Core`_:

- Odoo configurations for TurnKey v18.x:
- Odoo configurations for TurnKey v19.x:

- Odoo v16 installed from debian backports apt repo (v18.x).
- Odoo 19 Community installed from Odoo's official package repository.
- Includes modules from base install of Odoo.
- Includes the patched wkhtmltopdf 0.12.6 report renderer required by Odoo.

- **Security note**: As of 18.0, due to using the debian backports repo
Updates to Odoo **ARE NOT** configured to install automatically.
- **Security note**: Odoo application updates require supervision and are not
configured to install automatically. Refresh APT metadata, check the official
daily channel with ``odoo-update --check``, back up the database, then apply
the selected APT update.

- SSL support out of the box.
- `Adminer`_ administration frontend for PostgreSQL (listening on
port 12322 - uses SSL).
- Webmin modules for configuring Apache2, PostgreSQL and Postfix.

**To create a new Odoo Databse (i.e. site)**
**To create a new Odoo Database (i.e. site)**

To create a new Odoo DB, the Odoo config file needs to be edited and the
Odoo service restarted.

Overview of process:
1. Edit /etc/odoo/odoo
1. Edit /etc/odoo/odoo.conf
- change value of "db_name" from "TurnkeylinuxExample" to your desired DB
name
2. Restart odoo.service
Expand All @@ -52,7 +55,7 @@ Webmin steps:

CLI steps:
1.
- Edit /etc/odoo/odoo as per step 1
- Edit /etc/odoo/odoo.conf as per step 1
2.
- Restart odoo.service::

Expand Down
21 changes: 21 additions & 0 deletions changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
turnkey-odoo-19.0 (1) turnkey; urgency=low

* Install supported Odoo 19 Community from its official daily package
channel with an exact package digest and bound repository key.

* Install Odoo's required patched wkhtmltopdf 0.12.6 series from an exact
upstream package digest for PDF report rendering.

* Bridge Odoo's stale python3-pypdf2 package dependency to Trixie's
maintained python3-pypdf implementation without modifying Odoo's payload.

* Use a non-superuser PostgreSQL application role and keep generated
database and firstboot credentials out of process arguments and traces.

* Add a non-mutating update check, v19 acceptance coverage, and README
evidence crosswalk.

* Upgrade the base distribution to Debian 13 Trixie.

-- TurnKey Linux release engineering <release-engineering@turnkeylinux.org> Tue, 25 Aug 2026 00:00:00 +0000

turnkey-odoo-18.0 (1) turnkey; urgency=low

* Install Odoo v16.x from Debian backports (bookworm-backports) apt
Expand Down
182 changes: 155 additions & 27 deletions conf.d/main
Original file line number Diff line number Diff line change
@@ -1,11 +1,124 @@
#!/bin/bash -ex

set -o pipefail

# Install the supported Odoo Community package from Odoo's official channel.
ODOO_VERSION=19.0.20260825
ODOO_REPO=https://nightly.odoo.com/19.0/nightly/deb
ODOO_PACKAGE_URL="$ODOO_REPO/odoo_${ODOO_VERSION}_all.deb"
ODOO_PACKAGE_SHA256=e9d89da0fc94cd752b08b1e5501d97f464b834229ff8d68c7fecf24304e1da69
ODOO_KEY_URL=https://nightly.odoo.com/odoo.key
ODOO_KEY_SHA256=1d169c013a727fa00238ba3f6417d5f33f162f8fbae32159a87ecab6966bfe8e
ODOO_KEY_FINGERPRINT=5D134C924CB06330DCEFE2A1DEF2A2198183CBB5
WKHTMLTOX_VERSION=1:0.12.6.1-3.bookworm
WKHTMLTOX_ARCHITECTURE=amd64
WKHTMLTOX_URL=https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-3/wkhtmltox_0.12.6.1-3.bookworm_amd64.deb
WKHTMLTOX_SHA256=98ba0d157b50d36f23bd0dedf4c0aa28c7b0c50fcdcdc54aa5b6bbba81a3941d
PYPDF_COMPAT_PACKAGE=turnkey-odoo-pypdf-compat
PYPDF_COMPAT_VERSION=1.0+turnkey19.0.1
SOURCE_RECORD=/usr/local/share/turnkey-odoo/source

key=$(mktemp)
package=$(mktemp --suffix=.deb)
wkhtmltox=$(mktemp --suffix=.deb)
pypdf_compat_root=$(mktemp -d)
pypdf_compat=$(mktemp --suffix=.deb)
trap 'find "$key" "$package" "$wkhtmltox" "$pypdf_compat_root" "$pypdf_compat" -depth -delete' EXIT
curl -fsSL "$ODOO_KEY_URL" -o "$key"
echo "$ODOO_KEY_SHA256 $key" | sha256sum -c -
test "$(gpg --show-keys --with-colons "$key" |
awk -F: '$1 == "fpr" && !fingerprint { fingerprint=$10 } END { print fingerprint }')" = \
"$ODOO_KEY_FINGERPRINT"
gpg --batch --yes --dearmor --output /usr/share/keyrings/odoo-archive-keyring.gpg "$key"

cat >/etc/apt/sources.list.d/odoo.list <<EOF
deb [signed-by=/usr/share/keyrings/odoo-archive-keyring.gpg] $ODOO_REPO/ ./
EOF
cat >/etc/apt/preferences.d/odoo <<EOF
Package: *
Pin: origin "nightly.odoo.com"
Pin-Priority: 100

Package: odoo
Pin: origin "nightly.odoo.com"
Pin-Priority: 500
EOF

apt-get update
curl -fsSL "$WKHTMLTOX_URL" -o "$wkhtmltox"
echo "$WKHTMLTOX_SHA256 $wkhtmltox" | sha256sum -c -
test "$(dpkg-deb --field "$wkhtmltox" Package)" = wkhtmltox
test "$(dpkg-deb --field "$wkhtmltox" Version)" = "$WKHTMLTOX_VERSION"
test "$(dpkg-deb --field "$wkhtmltox" Architecture)" = "$WKHTMLTOX_ARCHITECTURE"
test "$(dpkg --print-architecture)" = "$WKHTMLTOX_ARCHITECTURE"
DEBIAN_FRONTEND=noninteractive apt-get install -y "$wkhtmltox"
test "$(dpkg-query -W -f='${Version}' wkhtmltox)" = "$WKHTMLTOX_VERSION"
wkhtmltopdf --version | grep -Fq 'wkhtmltopdf 0.12.6.1 (with patched qt)'

# Odoo's 19.0 payload supports pypdf and its own Trixie upgrade installs
# python3-pypdf, but the daily Debian package still names Bookworm's removed
# python3-pypdf2 package. Keep the verified Odoo payload intact and bridge only
# that stale, unversioned package name to Debian's maintained implementation.
mkdir -p "$pypdf_compat_root/DEBIAN"
cat >"$pypdf_compat_root/DEBIAN/control" <<EOF
Package: $PYPDF_COMPAT_PACKAGE
Version: $PYPDF_COMPAT_VERSION
Section: misc
Priority: optional
Maintainer: TurnKey GNU/Linux <admin@turnkeylinux.org>
Architecture: all
Depends: python3-pypdf
Provides: python3-pypdf2
Description: Odoo 19 pypdf dependency compatibility
Satisfies Odoo's stale python3-pypdf2 package dependency with Trixie's
maintained python3-pypdf implementation.
EOF
dpkg-deb --build --root-owner-group "$pypdf_compat_root" "$pypdf_compat"
test "$(dpkg-deb --field "$pypdf_compat" Package)" = "$PYPDF_COMPAT_PACKAGE"
test "$(dpkg-deb --field "$pypdf_compat" Version)" = "$PYPDF_COMPAT_VERSION"
test "$(dpkg-deb --field "$pypdf_compat" Architecture)" = all
test "$(dpkg-deb --field "$pypdf_compat" Depends)" = python3-pypdf
test "$(dpkg-deb --field "$pypdf_compat" Provides)" = python3-pypdf2
DEBIAN_FRONTEND=noninteractive apt-get install -y "$pypdf_compat"
test "$(dpkg-query -W -f='${Version}' "$PYPDF_COMPAT_PACKAGE")" = \
"$PYPDF_COMPAT_VERSION"
test "$(dpkg-query -W -f='${Provides}' "$PYPDF_COMPAT_PACKAGE")" = \
python3-pypdf2
python3 -c 'import pypdf; assert pypdf.__version__'

curl -fsSL "$ODOO_PACKAGE_URL" -o "$package"
echo "$ODOO_PACKAGE_SHA256 $package" | sha256sum -c -
DEBIAN_FRONTEND=noninteractive apt-get install -y "$package"
test "$(dpkg-query -W -f='${Version}' odoo)" = "$ODOO_VERSION"
apt-get check
runuser -u odoo -- python3 -c \
'from odoo.tools import pdf; assert pdf.SUBMOD == "._pypdf"; assert pdf.pypdf.__version__'
systemctl stop odoo

mkdir -p "$(dirname "$SOURCE_RECORD")"
cat >"$SOURCE_RECORD" <<EOF
package_source=$ODOO_REPO
installed_version=$ODOO_VERSION
package_url=$ODOO_PACKAGE_URL
package_sha256=$ODOO_PACKAGE_SHA256
repository_key_fingerprint=$ODOO_KEY_FINGERPRINT
repository_key_sha256=$ODOO_KEY_SHA256
wkhtmltox_version=$WKHTMLTOX_VERSION
wkhtmltox_architecture=$WKHTMLTOX_ARCHITECTURE
wkhtmltox_url=$WKHTMLTOX_URL
wkhtmltox_sha256=$WKHTMLTOX_SHA256
pypdf_compat_package=$PYPDF_COMPAT_PACKAGE
pypdf_compat_version=$PYPDF_COMPAT_VERSION
EOF
chmod 0644 "$SOURCE_RECORD"

# Set variables
#
set +x
DB_USER=odoo
DB_PASS=$(mcookie)
DB_NAME="TurnkeylinuxExample"
[[ $DB_PASS =~ ^[[:xdigit:]]+$ ]]

# We will use a random password for now. Reset during inithook
ODOO_ADMIN_PASSWORD=$(mcookie)
Expand All @@ -21,34 +134,58 @@ CONF=/etc/odoo/odoo.conf
# Start postgresql server
systemctl start postgresql

# Create db user
su postgres -c "createuser --superuser --createdb --no-createrole $DB_USER"
# The official package creates this role in postinst. Keep the configuration
# safe if that maintainer-script behavior changes, and enforce least privilege
# in either case.
if [[ $(runuser -u postgres -- psql --no-psqlrc --tuples-only --no-align \
postgres --command="SELECT 1 FROM pg_roles WHERE rolname = '$DB_USER';") != 1 ]]; then
runuser -u postgres -- createuser --no-superuser --createdb \
--no-createrole "$DB_USER"
fi

# Set db user password
su postgres -c "psql postgres" << EOF
alter user $DB_USER with encrypted password '$DB_PASS';
runuser -u postgres -- psql --no-psqlrc --set ON_ERROR_STOP=on postgres <<EOF
ALTER ROLE $DB_USER WITH LOGIN NOSUPERUSER CREATEDB NOCREATEROLE
NOREPLICATION PASSWORD '$DB_PASS';
EOF
su postgres -c "psql postgres" << EOF
create database "$DB_NAME" owner $DB_USER;

if [[ -n $(runuser -u postgres -- psql --no-psqlrc --tuples-only --no-align \
postgres --command="SELECT 1 FROM pg_database WHERE datname = '$DB_NAME';") ]]; then
echo "refusing to replace existing database: $DB_NAME" >&2
exit 1
fi
runuser -u postgres -- createdb --owner="$DB_USER" "$DB_NAME"

runuser -u postgres -- psql --no-psqlrc --set ON_ERROR_STOP=on \
"$DB_NAME" <<EOF
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
GRANT CREATE ON SCHEMA public TO $DB_USER;
EOF

# Set password/name in config file
sed -i "s|db_password =.*|db_password = $DB_PASS|" $CONF
sed -i "s|db_host =.*|db_host = localhost|" $CONF
echo "db_name = $DB_NAME" >> $CONF
cat >>"$CONF" <<EOF
proxy_mode = True
workers = 2
max_cron_threads = 1
gevent_port = 8072
EOF

# Stop postgresql server
systemctl stop postgresql

# Set admin password to random value for now
sed -i "s|admin_passwd =.*|admin_passwd = $ODOO_ADMIN_PASSWORD|" $CONF
chown odoo:odoo $CONF
chown root:odoo $CONF
chmod 0640 $CONF

# Add and disable service user
usermod -L $SERVICE_USER

# Enable needed modules
a2enmod proxy_http headers rewrite
a2enmod proxy_http proxy_wstunnel headers rewrite

# Configure sites
a2dissite 000-default
Expand All @@ -58,30 +195,21 @@ a2ensite odoo.conf
systemctl start postgresql
systemctl start apache2

su odoo -g odoo -s '/bin/bash' -c 'odoo -i base \
--stop-after-init --db_host=localhost \
-d '"\"$DB_NAME\""' \
-r '"\"$DB_USER\""' \
-w '"\"$DB_PASS\""' \
-s -c '"\"$CONF\""
runuser -u odoo -- odoo -i base --stop-after-init \
-d "$DB_NAME" -c "$CONF"

# enable and start odoo
systemctl daemon-reload
systemctl enable odoo
systemctl start odoo

while [[ "$(curl -Lk http://localhost)" = *"Unavailable"* ]]; do
sleep 1
for attempt in {1..60}; do
curl --insecure --fail --silent https://127.0.0.1/web/login >/dev/null && break
sleep 2
done

URL="https://127.0.0.1/web"
CURL="curl --insecure -c /tmp/cookie -b /tmp/cookie"

$CURL $URL/login
sleep 1
$CURL $URL/database/manager
sleep 1
$CURL $URL/database/change_password --data-raw "master_pwd=admin&master_pwd_new=${ODOO_ADMIN_PASSWORD}"
curl --insecure --fail --silent https://127.0.0.1/web/login >/dev/null

systemctl stop odoo
systemctl stop postgresql
systemctl stop apache2
systemctl stop apache2
set -x
Loading