Administration
This page covers tasks a server administrator may need to perform to keep MiniMost running smoothly.
Automatic Cleanup
MiniMost runs a background cleanup thread that starts 5 minutes after startup and repeats every 24 hours. No cron job or external scheduler is required. It performs two jobs:
File cleanup — removes old attachments from
uploads/.Message cleanup — permanently deletes old rows from every
users/*.db.
All retention periods are read from settings.json on each run, so changes
take effect at the next scheduled run without restarting the server. See
Configuration for the full list of keys and defaults.
File Cleanup
Separate retention periods apply to different file types (both default: 30 days):
"image_retention_days"— images (jpg, jpeg, png, gif, webp)"file_retention_days"— all other file types (pdf, zip, docx, etc.)
Running file cleanup manually:
python3 src/minimost/clean.py
Dry run (preview without deleting):
from minimost.clean import delete_files_older_than
delete_files_older_than("uploads", image_days=30, file_days=30, dry_run=True)
Custom retention:
from minimost.clean import delete_files_older_than
# Keep images for 90 days, delete other files after 7 days
delete_files_older_than("uploads", image_days=90, file_days=7)
Note
File cleanup operates on filesystem mtime values. Deleted files leave
behind orphan database rows — messages referencing deleted files show a
“File deleted” indicator rather than being removed from chat history.
Message Cleanup
Old messages are permanently deleted from every users/*.db to prevent
database files from growing without bound. The retention period is set by
"message_retention_days" in settings.json (default: 770 days).
Running message cleanup manually:
from minimost.clean import delete_messages_older_than
delete_messages_older_than("users", days=770)
Dry run (preview without deleting):
from minimost.clean import delete_messages_older_than
delete_messages_older_than("users", days=770, dry_run=True)
Warning
Message cleanup is a permanent hard delete — rows are removed from the
database entirely and cannot be recovered. Ensure your message_retention_days
value is set to a period that comfortably covers how far back your users
ever need to scroll before lowering it.
User Management
MiniMost has no admin UI. User management is done directly via SQLite.
List all users:
sqlite3 auth.db "SELECT username FROM users;"
Reset a forgotten password:
Use the built-in CLI command to generate a one-time, time-limited reset URL:
minimost reset-password alice
This stores a secure token in auth.db, sends the user a system DM notifying
them that a reset was requested, and prints a URL to stdout. Share that URL with
the user through a side-channel (email, phone, etc.). When they open it they can
set a new password; the link is invalidated immediately after use.
By default the link expires in 60 minutes. Adjust with --expires:
minimost reset-password alice --expires 30
If the server is not on 127.0.0.1:5000, provide the public base URL so the
printed link is correct:
minimost reset-password alice --base-url https://chat.example.com
Run minimost reset-password --help for the full list of options.
Note
Reset tokens are single-use and stored in the password_reset_tokens table
in auth.db. Expired tokens are never automatically purged, but they are
harmless — they are rejected at validation time. To clean them up manually:
sqlite3 auth.db "DELETE FROM password_reset_tokens WHERE used = 1 OR expires_ts < unixepoch();"
Account Deletion
Users can delete their own account from Settings → Danger Zone. Two modes are available, both requiring the user to enter their current password before anything is changed.
Soft delete
Removes the user’s login credentials, settings, and avatar. Every message they
sent is re-attributed to Deleted User across all recipient databases.
Chat history remains intact and visible to other users. The account cannot be
recovered, but a new account with the same username can be registered later.
Hard delete
Removes the user’s login credentials, settings, and avatar, and additionally deletes every message they ever sent from every channel and conversation across all user databases. Private channels the user created are left intact (other members’ messages are unaffected); only the deleted user’s own messages are removed.
Note
After either deletion the user’s session is immediately invalidated and they are redirected to the login page. Private channel memberships and presence records are cleaned up automatically in both cases.
Manual deletion (admin override)
If an account must be removed by an administrator without the user’s co-operation, replicate the hard delete steps directly against the databases. The self-service flow above is the recommended path whenever possible.
# 1. Remove credentials and settings
sqlite3 auth.db "DELETE FROM users WHERE username = 'alice';"
sqlite3 auth.db "DELETE FROM user_settings WHERE username = 'alice';"
sqlite3 auth.db "DELETE FROM password_reset_tokens WHERE username = 'alice';"
# 2. Remove messages from every user database
for db in users/*.db; do
sqlite3 "$db" "DELETE FROM messages WHERE sender = 'alice';"
done
# 3. Delete the user's own database file and avatar
rm -f users/alice.db
# avatar filename is stored in user_settings.avatar_file — check before deleting
rm -f avatars/<avatar_filename>
# 4. Remove presence records
sqlite3 presence.db "DELETE FROM presence WHERE user = 'alice';"
sqlite3 presence.db "DELETE FROM typing WHERE user = 'alice';"
sqlite3 presence.db "DELETE FROM read_receipts WHERE reader = 'alice';"
sqlite3 presence.db "DELETE FROM message_reactions WHERE reactor = 'alice';"
sqlite3 presence.db "DELETE FROM private_channel_members WHERE username = 'alice';"
sqlite3 presence.db "DELETE FROM call_participants WHERE username = 'alice';"
Add a channel:
Edit the "channels" list in settings.json and restart the server:
# settings.json (before)
{"channels": ["general", "software"], "image_retention_days": 30, "file_retention_days": 30, "message_retention_days": 770}
# settings.json (after)
{"channels": ["general", "software", "design"], "image_retention_days": 30, "file_retention_days": 30, "message_retention_days": 770}
Remove a channel:
Edit the "channels" list in settings.json, removing the unwanted channel
name, and restart the server. Existing messages for that channel remain in all
user databases but will no longer be accessible through the UI.
Database Maintenance
Check WAL file sizes:
If a user’s database has a large .wal file, it means the WAL has not
been checkpointed. Force a checkpoint:
sqlite3 users/alice.db "PRAGMA wal_checkpoint(FULL);"
Compact a database:
After many soft-deletes, a database may grow larger than necessary. Run
VACUUM to reclaim space:
sqlite3 users/alice.db "VACUUM;"
Note: VACUUM rewrites the entire database file and can take a while on
large databases.
Check for corruption:
sqlite3 auth.db "PRAGMA integrity_check;"
sqlite3 presence.db "PRAGMA integrity_check;"
for db in users/*.db; do
echo "Checking $db..."
sqlite3 "$db" "PRAGMA integrity_check;"
done
Backup and Restore
Backup:
All state lives in these locations:
# Full backup
tar -czf minimost-backup-$(date +%Y%m%d).tar.gz \
auth.db \
presence.db \
secret.key \
cert.pem \
key.pem \
settings.json \
users/ \
uploads/ \
avatars/
Restore:
tar -xzf minimost-backup-20240101.tar.gz -C /srv/minimost/
Hot backup (without stopping the server):
SQLite WAL mode allows safe online backups using the .backup command:
sqlite3 auth.db ".backup /backup/auth.db"
sqlite3 presence.db ".backup /backup/presence.db"
for db in users/*.db; do
name=$(basename "$db")
sqlite3 "$db" ".backup /backup/users/$name"
done
Monitoring
Check server status (systemd):
systemctl status minimost
journalctl -u minimost -f
Check disk usage:
du -sh users/ uploads/ auth.db presence.db
Count messages per user:
for db in users/*.db; do
user=$(basename "$db" .db)
count=$(sqlite3 "$db" "SELECT COUNT(*) FROM messages WHERE deleted=0;")
echo "$user: $count messages"
done
Migrating to a New Server
Install MiniMost on the new server.
Stop the old server.
Copy all data files:
rsync -avz \ auth.db \ presence.db \ secret.key \ cert.pem \ key.pem \ settings.json \ users/ \ uploads/ \ avatars/ \ newserver:/srv/minimost/
Start the new server.
Verify that existing sessions work (they will, because
secret.keywas preserved).
If you do not copy secret.key, a new key will be generated and all
existing browser sessions will be invalidated — users will have to log in
again.