Best practices
This page covers security considerations, operational best practices, and deployment guidance for Canasta instances. For foundational concepts and architecture, see General concepts.
Security
Password storage
- Admin passwords are stored in plaintext files at
admin-password_{wikiid} - Database passwords are stored in plaintext in the
.envfile - Restrict access to the instance directory using file permissions:
# Owner can read/write/traverse; group (www-data) can read/traverse; others have no access
chmod 750 /path/to/instance
chmod 640 /path/to/instance/.env
chmod 640 /path/to/instance/admin-password_*
- If using GitOps, ensure that files containing passwords (
.env,admin-password_*) are excluded from version control. The default.gitignorecreated bycanasta gitops initalready excludes these files. - Avoid passing passwords directly on the command line — they may appear in shell history and process listings. Use environment variables instead:
canasta create -i myinstance -w main --rootdbpass "$ROOT_DB_PASS"
Docker access
Running Canasta CLI commands does not require sudo. On Linux, your user must be in the docker and web server groups. See the Installation guide for setup instructions and the Troubleshooting page if you encounter permission errors.
Network exposure
- By default, Canasta exposes ports for HTTP/HTTPS traffic
- Caddy handles SSL/TLS termination automatically
- To customize Caddy behavior (headers, redirects, etc.), edit
config/Caddyfile.site— do not editconfig/Caddyfiledirectly as it is regenerated when wikis change - Consider adding security headers in
config/Caddyfile.site:
header X-Frame-Options "SAMEORIGIN" header X-Content-Type-Options "nosniff" header Referrer-Policy "strict-origin-when-cross-origin"
- Review your
docker-compose.override.ymlif you need to customize port bindings or network settings
Operations
Backups
- Set up regular backups using
canasta backup - Always take a backup before running
canasta upgrade - For production wikis, schedule automated backups using
canasta backup schedule setso you have recent restore points - Store backup passwords securely and separately from your server
- Test your backup restoration process periodically — an untested backup is not a backup
- See the Backup guide for full details on creating, restoring, and managing backups
GitOps
For production wikis, consider using GitOps to track your instance configuration in a Git repository. This provides version history for settings changes, makes it easy to replicate an instance, and enables collaborative configuration management. See the GitOps guide for setup instructions.
Upgrading
- Check release notes before upgrading — some releases may require manual steps
- Always back up before upgrading
- For production wikis, test upgrades on a staging instance first (see Common tasks: Running on non-standard ports for running multiple instances)
- See the Upgrading guide for pre-upgrade steps and the full upgrade process
Database tuning
MariaDB runs at its compiled defaults unless you tell it otherwise. The one that matters most for a large wiki is innodb_buffer_pool_size, which is 128 MB regardless of how big the database is.
That default bites harder than it looks. InnoDB uses O_DIRECT on Linux, so it bypasses the operating system's page cache — the buffer pool is its only cache, and free RAM on the host does not help. A host can look comfortably provisioned while the database reads from disk continuously. On one 17.6 GB instance the buffer-pool hit rate read as a healthy 98.3%, while the missing 1.7% was 10.8 million disk reads that re-read the entire dataset roughly ten times in fifteen hours.
Checking the current value
canasta maintenance exec -i <instance-id> -s db -- \
sh -c 'mariadb -u root -p"$MYSQL_ROOT_PASSWORD" -N -B \
-e "SHOW VARIABLES LIKE \"innodb_buffer_pool_size\""'
The value is in bytes: 134217728 is the 128 MB default. On Kubernetes the variable holding the password inside the container is $MYSQL_PASSWORD rather than $MYSQL_ROOT_PASSWORD.
Docker Compose
Each instance has a my.cnf in its directory, bind-mounted into the database container at /etc/my.cnf. As shipped it contains only a [client] header. That is the one group mariadbd does not read — it reads [mysqld] and a few related groups — so a server setting placed under the existing [client] header does not reach the server.
It does something worse than nothing. The client tools do read [client], and they reject options they do not recognize, so innodb_buffer_pool_size there breaks every mariadb and mariadb-dump invocation:
mariadb: unknown variable 'innodb_buffer_pool_size=4G'
Backups, restores and maintenance queries all use those tools, so they begin failing while the server itself keeps serving normally and nothing else reports it. Add a [mysqld] section instead:
[client]
[mysqld]
innodb_buffer_pool_size = 1G
Then restart the instance:
canasta restart -i <instance-id>
Kubernetes
The chart ships its own my.cnf and the CLI does not sync a hand-edited instance copy to the cluster. Add the file to configData.db in the instance's values.yaml instead:
configData:
db:
my.cnf: |
[client]
[mysqld]
innodb_buffer_pool_size = 1G
Setting configData.db replaces the chart's default file entirely, so include the whole contents, not just the lines you are changing. The CLI generates a separate values file for the config it manages (web, env, Caddy, Varnish, CrowdSec) and never sets db, so your entry is preserved across deployments. Then restart the instance:
canasta restart -i <instance-id>
Restart, not reconcile
On Docker Compose the change needs canasta restart. canasta reconcile does not apply it, and reports "config and running containers are in sync" while the server keeps running with the old value: nothing in the service definition changed, so the database container is not recreated and never re-reads my.cnf.
On Kubernetes, canasta reconcile is enough. The database pod carries a checksum of its config, so a change to configData.db rolls the pod and the new file takes effect. (Before that checksum existed, the ConfigMap updated while the running pod kept the old file — if you are on an older release, use canasta restart there too.)
Always confirm the value afterwards with the command above. This is not a formality: InnoDB rounds the value up to a multiple of its chunk size and silently clamps a value the container cannot allocate, so a setting that the file accepted but the server did not looks identical to one that worked.
my.cnf is captured by canasta backup create and restored with the instance, and under GitOps it is a tracked file like any other.
Choosing a value
There is no single right answer, but the trade-offs are narrow:
- Size it against the data, not the host. A buffer pool larger than the database wastes memory the rest of the stack could use. Total InnoDB size is a reasonable ceiling.
- Leave room for everything else. The database shares the host with the
webcontainer's PHP-FPM workers, Caddy, and — where enabled — Varnish, Elasticsearch, or the full OpenSearch stack. The "70–80% of RAM" advice written for dedicated database servers does not apply here. - Watch for the ceiling that binds. If the database service or pod has a memory limit, that limit governs, not host RAM.
Managing multiple instances
- Use descriptive instance IDs that indicate the purpose (e.g.,
company-wiki,docs-internal) - Keep a record of which wikis are in each instance if using wiki farms
- Use
canasta listregularly to review your instances
Wiki ID naming rules
Wiki IDs may contain only alphanumeric characters and underscores. Hyphens (-) are not allowed (they are allowed in instance IDs, but not wiki IDs). The following names are reserved and cannot be used: settings, images, w, wiki.
Valid examples: mywiki, wiki_1, MyWiki2024, docs
Invalid examples: my-wiki, my wiki, wiki!, settings
Note: Instance IDs (the -i flag) have different rules — they allow hyphens and underscores, must start and end with an alphanumeric character, and have no reserved names.