Publishing From a Phone
Chinaski can run entirely on an Android phone: the admin, the database, and the build all live in the phone's storage, while the public site itself is served from Cloudflare R2 object storage. Editors open the admin in the phone's browser to write and publish; visitors never touch the phone at all, so the public site stays up even if the phone is off, out of battery, or offline.
This is a one-time setup, best done by whoever is comfortable with a few terminal commands. After setup, day-to-day publishing needs none of that, just the admin in a browser and a home-screen shortcut.
If a laptop is available, see Publishing From a Laptop instead. It is the same zero-cost setup, with a real keyboard and screen.
Do the first install on Wi-Fi: it downloads the full offline bundle and compiles several Perl modules.
1. Install the Android apps
Install Termux, Termux:Boot, and Termux:Widget, all three from the same source, preferably F-Droid (a GitHub release also works; just don't mix sources, since their signing keys differ). The Google Play Store's Termux listing is a separate build with its own package repository and isn't used here.
Open Termux:Boot once after installing it, and in Android's app settings, exempt Termux and Termux:Boot from battery optimization. Otherwise Android may stop the admin in the background.
2. Install Chinaski
In Termux, run:
curl -fsSL https://www.chinaski.net/dist/termux-quickstart.sh | bash This one command installs the packages Chinaski needs, downloads and checksum-verifies the offline bundle, builds Chinaski's Perl dependencies on the phone (the slow part, a few minutes), sets up the database, starts the admin once to confirm it works, and prepares the boot and home-screen launchers from step 3. No root is needed.
When it finishes, open http://127.0.0.1:8080/admin in the phone's browser. The admin only listens on the phone itself, so it's never reachable from outside. If it doesn't load, read $HOME/chinaski/chinaski.log.
Manual steps (the same as the command above; useful if something fails)
pkg update
pkg install perl make clang sqlite openssl-tool libjpeg-turbo libpng giflib libwebp rclone rsync curl tar gzip
cd "$HOME"
curl -fSLO https://www.chinaski.net/dist/chinaski-latest.tar.gz
curl -fSLO https://www.chinaski.net/dist/chinaski-latest.tar.gz.sha256
sha256sum -c chinaski-latest.tar.gz.sha256
mkdir extracted
tar -xzf chinaski-latest.tar.gz -C extracted
cd extracted/chinaski-*
bash install.sh --termux --prefix "$HOME/chinaski"
bash "$HOME/chinaski/app/install/termux-start.sh"
mkdir -p "$HOME/.termux/boot" "$HOME/.shortcuts/tasks"
cp "$HOME/chinaski/app/install/termux-start.sh" "$HOME/.termux/boot/10-chinaski"
cp "$HOME/chinaski/app/install/termux-start.sh" "$HOME/.shortcuts/tasks/Restart My Website"
chmod 700 "$HOME/.termux/boot/10-chinaski" "$HOME/.shortcuts/tasks/Restart My Website" If the image library (Imager) fails to compile, save the full build log and the output of pkg list-installed for troubleshooting.
3. Keep it running
The install already placed the launcher where Termux:Boot and Termux:Widget look for it. Two one-time steps on the phone finish the job:
- Open the Termux:Boot app once, so the admin starts again after a reboot.
- Add the Termux:Widget widget to the home screen and pick Restart My Website: one tap restarts the admin if it's ever stopped, no terminal needed. If the admin is already running, the tap does nothing.
4. Point it at Cloudflare R2
This is where the public site actually lives.
In Cloudflare:
- Create an R2 bucket with Standard storage. Use this bucket for this site only: every publish makes the bucket match the site exactly and deletes anything else in it.
- Under R2 → Manage API tokens, create an API token with Object Read & Write permission, limited to that bucket. Copy the Access Key ID and Secret Access Key. The secret is shown only once. Note your Account ID from the R2 overview page.
- In the bucket's Settings, connect a custom domain (for example
www.example.com). Cloudflare creates the DNS record and makes the bucket public on that address, with nothing else to switch on. R2's ownr2.devaddress is for testing only. - R2 serves a file only by its exact name, while Chinaski saves pages as
.../index.html. On your domain (under Rules, not R2), add a URL Rewrite Rule:- Filter (custom expression):
(http.host eq "www.example.com" and ends_with(http.request.uri.path, "/"))(with your own address) - Path: rewrite to Dynamic
concat(http.request.uri.path, "index.html")
- Filter (custom expression):
In Chinaski: set Settings → General → Site URL to your public address (for example https://www.example.com), then in Settings → Deploy:
| Field | Value |
|---|---|
| Deploy automatically after a successful build | On |
| Enable S3 deploy | On |
| Bucket | Your R2 bucket name |
| Endpoint | https://<ACCOUNT_ID>.r2.cloudflarestorage.com (nothing after .com) |
| Region | auto |
| Access Key / Secret Key | The R2 API credentials |
Don't put the bucket name on the end of the Endpoint. Cloudflare sometimes shows the address as https://<ACCOUNT_ID>.r2.cloudflarestorage.com/<bucket>. Copied like that, the site is uploaded into a folder inside the bucket and every page shows R2's "Error 404: Is this your bucket?" message, even though Test S3 Connection passes.
Save, then use Test S3 Connection to confirm the credentials work. Publish something, then open your address and a page inside the site to confirm everything loads.
Publishing a change
Nothing phone-specific here: edit and save a page or post as normal, and the build and deploy run the same way they would on a server. Watch the admin's status banner for "Publishing your update…" through to "Your update is live." A slow mobile connection can make that step take a while; the banner stays accurate for as long as the upload is genuinely still running.
If a build succeeds but the upload fails (connection dropped mid-upload, say), the site last published to R2 stays exactly as it was, and visitors never see a half-finished publish. Retry once you're back on a connection.
Updating Chinaski
To install the latest Chinaski release, run this in Termux:
curl -fsSL https://www.chinaski.net/dist/chinaski-update.sh | bash It checks whether a new release is available, installs it, and restarts the admin. Your database, settings, uploads and media are kept. Installs made before rsync was part of the package list need it once: pkg install rsync.
Good to know
- Forms and document downloads: the public site is served by R2, and the phone isn't reachable from the internet, so contact forms and document downloads on the public site don't work in this setup.
- Costs: Cloudflare R2's free tier covers a typical small site many times over, and traffic to visitors isn't charged. You still pay for your domain name.