WriteFreely Deployment
WriteFreely is a clean, minimalist publishing platform made for writers. This deployment provides a fully functional WriteFreely instance with persistent storage, SSL certificates, and admin access.
🚀 Access Information
- Blog URL:
https://blog.keyboardvagabond.com - Admin Username:
mdileo - Admin Password: Stored in
writefreely-secretKubernetes secret
📁 File and Folder Locations
Inside the Pod
/writefreely/ # WriteFreely application directory
├── writefreely # Main binary executable
├── writefreely-docker.sh # Docker entrypoint script
├── static/ # CSS, JS, fonts, images
├── templates/ # HTML templates
├── pages/ # Static pages
└── keys/ # Application encryption keys (symlinked to /data/keys)
/data/ # Persistent volume mount (survives pod restarts)
├── config.ini # Main configuration file (writable)
├── writefreely.db # SQLite database
└── keys/ # Encryption keys directory
├── email.aes256 # Email encryption key
├── cookies.aes256 # Cookie encryption key
├── session.aes256 # Session encryption key
└── csrf.aes256 # CSRF protection key
Kubernetes Resources
manifests/applications/write-freely/
├── namespace.yaml # writefreely-system namespace
├── deployment.yaml # Main application deployment
├── service.yaml # ClusterIP service (port 8080)
├── ingress.yaml # NGINX ingress with SSL
├── storage.yaml # PersistentVolumeClaim for data
├── secret.yaml # Admin password (SOPS encrypted)
├── configmap.yaml # Configuration template (unused in current setup)
├── kustomization.yaml # Kustomize resource list
└── README.md # This file
⚙️ Configuration Management
Edit config.ini
To edit the WriteFreely configuration file:
# Get current pod name
POD_NAME=$(kubectl -n writefreely-system get pods -l app=writefreely -o jsonpath='{.items[0].metadata.name}')
# Edit config.ini directly
kubectl -n writefreely-system exec -it $POD_NAME -- vi /data/config.ini
# Or copy out, edit locally, and copy back
kubectl -n writefreely-system cp $POD_NAME:/data/config.ini ./config.ini
# Edit config.ini locally
kubectl -n writefreely-system cp ./config.ini $POD_NAME:/data/config.ini
View current configuration
POD_NAME=$(kubectl -n writefreely-system get pods -l app=writefreely -o jsonpath='{.items[0].metadata.name}')
kubectl -n writefreely-system exec $POD_NAME -- cat /data/config.ini
Restart after config changes
kubectl -n writefreely-system rollout restart deployment writefreely
🔧 Admin Commands
WriteFreely includes several admin commands for user and database management:
Create additional users
POD_NAME=$(kubectl -n writefreely-system get pods -l app=writefreely -o jsonpath='{.items[0].metadata.name}')
# Create admin user
kubectl -n writefreely-system exec $POD_NAME -- /writefreely/writefreely -c /data/config.ini user create --admin username:password
# Create regular user (requires existing admin)
kubectl -n writefreely-system exec $POD_NAME -- /writefreely/writefreely -c /data/config.ini user create username:password
Reset user password
POD_NAME=$(kubectl -n writefreely-system get pods -l app=writefreely -o jsonpath='{.items[0].metadata.name}')
kubectl -n writefreely-system exec -it $POD_NAME -- /writefreely/writefreely -c /data/config.ini user reset-pass username
Database operations
POD_NAME=$(kubectl -n writefreely-system get pods -l app=writefreely -o jsonpath='{.items[0].metadata.name}')
# Initialize database (if needed)
kubectl -n writefreely-system exec $POD_NAME -- /writefreely/writefreely -c /data/config.ini db init
# Migrate database schema
kubectl -n writefreely-system exec $POD_NAME -- /writefreely/writefreely -c /data/config.ini db migrate
📊 Monitoring and Logs
View application logs
# Live logs
kubectl -n writefreely-system logs -f -l app=writefreely
# Recent logs
kubectl -n writefreely-system logs -l app=writefreely --tail=100
Check pod status
kubectl -n writefreely-system get pods -l app=writefreely
kubectl -n writefreely-system describe pod -l app=writefreely
Check persistent storage
POD_NAME=$(kubectl -n writefreely-system get pods -l app=writefreely -o jsonpath='{.items[0].metadata.name}')
# Check data directory contents
kubectl -n writefreely-system exec $POD_NAME -- ls -la /data/
# Check database size
kubectl -n writefreely-system exec $POD_NAME -- du -h /data/writefreely.db
# Check encryption keys
kubectl -n writefreely-system exec $POD_NAME -- ls -la /data/keys/
🔐 Security
Password Management
The admin password is stored in a Kubernetes secret:
# View current password (base64 encoded)
kubectl -n writefreely-system get secret writefreely-secret -o jsonpath='{.data.admin-password}' | base64 -d
# Update password (regenerate secret)
# Edit manifests/applications/write-freely/secret.yaml and apply
SSL Certificates
SSL certificates are automatically managed by cert-manager and Let's Encrypt:
# Check certificate status
kubectl -n writefreely-system get certificates
kubectl -n writefreely-system describe certificate writefreely-tls
🔄 Backup and Restore
Database Backup
POD_NAME=$(kubectl -n writefreely-system get pods -l app=writefreely -o jsonpath='{.items[0].metadata.name}')
# Backup database
kubectl -n writefreely-system exec $POD_NAME -- cp /data/writefreely.db /data/writefreely-backup-$(date +%Y%m%d).db
# Copy backup locally
kubectl -n writefreely-system cp $POD_NAME:/data/writefreely-backup-$(date +%Y%m%d).db ./writefreely-backup-$(date +%Y%m%d).db
Full Data Backup
The entire /data directory is stored in a Longhorn persistent volume with automatic S3 backup to Backblaze B2.
🐛 Troubleshooting
Common Issues
- "Unable to load config.ini": Ensure config file exists in
/data/config.iniand is writable - "Username admin is invalid": Use non-reserved usernames (avoid "admin", "administrator")
- "Read-only file system": Config file must be in writable location (
/data/config.ini) - CSS/JS not loading: Check ingress configuration and static file serving
Reset to Clean State
# Delete pod to force recreation
kubectl -n writefreely-system delete pod -l app=writefreely
# If needed, delete persistent data (WARNING: This will delete all blog content)
# kubectl -n writefreely-system delete pvc writefreely-data
Debug Commands
POD_NAME=$(kubectl -n writefreely-system get pods -l app=writefreely -o jsonpath='{.items[0].metadata.name}')
# Check environment variables
kubectl -n writefreely-system exec $POD_NAME -- env | grep WRITEFREELY
# Check file permissions
kubectl -n writefreely-system exec $POD_NAME -- ls -la /data/
kubectl -n writefreely-system exec $POD_NAME -- ls -la /writefreely/
# Interactive shell for debugging
kubectl -n writefreely-system exec -it $POD_NAME -- sh
⚠️ Critical Configuration Settings
Theme Configuration (Required)
Important: The theme setting must not be empty or CSS/JS files will not load properly.
[app]
theme = write
Symptoms of missing theme:
- CSS files return 404 or malformed URLs like
/css/.css - Blog appears unstyled
- JavaScript not loading
Fix: Edit the config file and set theme = write:
POD_NAME=$(kubectl -n writefreely-system get pods -l app=writefreely -o jsonpath='{.items[0].metadata.name}')
kubectl -n writefreely-system exec -it $POD_NAME -- vi /data/config-writable.ini
# Add or update in the [app] section:
# theme = write
# Restart after changes
kubectl -n writefreely-system rollout restart deployment writefreely
📝 Configuration Reference
Key configuration sections in config.ini:
- [server]: Host, port, and TLS settings
- [database]: Database connection and file paths
- [app]: Site name, description, federation settings
- [auth]: User authentication and registration settings
- [federation]: ActivityPub and federation configuration
- [users]: User creation and management settings
For detailed configuration options, see the WriteFreely documentation.