Saturday, 5 September 2026
Deploying blojsom 2.28 (2006) on a modern host, 2026
Live instance: https://blojsom-production.up.railway.app/blojsom/blog/
Verdict up front: very possible, comfortably a weekend project, realistically an afternoon. Zero Java source changes. Zero build-file changes. Zero changes to any file the project shipped. Everything added is three new files in deploy/ plus a Dockerfile, and every "change" is either container plumbing or a value the original INSTALL instructions told you to edit by hand anyway.
Wall-clock for this run: roughly 25 minutes from git clone to a post written through the live admin UI surviving a container restart, including two dead ends noted below.
What blojsom 2.28 is, technically
- Servlet 2.3 webapp, Java source/target 1.4, built with Ant (
build.xml, no Maven, no downloads: every dependency is a jar checked intowar/WEB-INF/lib). - No database. Entries are files on disk under
blog-home; comments, trackbacks, and metadata are sibling dot-directories. Config is.propertiesfiles underWEB-INF/. - Velocity 1.4 templates, log4j 1.2.8, commons-*, xmlrpc 1.2. All pre-2006.
That profile is why this is easy: a self-contained, dependency-vendored, file-backed WAR is close to the best case for resurrecting old Java.
The steps
- Find the right tree. The GitHub repo is an SVN dump. 2.28 lives at
branches/blojsom-2_28-final/blojsom-2.0/(thetags/directory stops at 2.19; later releases are underbranches/). Trunk is 3.x, a different animal (Spring + Hibernate + SQL).
- Pick a JDK that still compiles
-source 1.4. JDK 9+ removed it (javac9 minimum is 6, current is 8). JDK 8 accepts it with warnings. So:eclipse-temurin:8-jdk.apt-get install ant,ant war. Build succeeds first try, 2 seconds, four "obsolete option" warnings. Output:distro/blojsom.war.
- Pick a servlet container that still speaks
javax.servlet. Tomcat 10+ isjakarta.*and will not load this WAR (Tomcat 10's auto-migration converter would probably work, but that is a change). Tomcat 9.0.x is still maintained and runs on JDK 8:tomcat:9.0-jdk8-temurin. Drop the WAR inwebapps/, done. Theweb.xmlis DTD-era 2.3 and Tomcat 9 accepts it without complaint.
- Give it somewhere to write.
blog.propertiesships withblog-home=/change/me/please/. On a PaaS the container filesystem is ephemeral, so this has to be a mounted volume. Railway: one volume mounted at/data,blog-home=/data/blog/.
- Tell it its own URL. blojsom builds absolute links (including the admin login form's
action) fromblog-base-url/blog-urlinblog.properties. Wrong value = admin login posts tolocalhost:8080and fails silently.deploy/entrypoint.shrewrites these at boot fromBLOJSOM_BASE_URL(falls back to Railway'sRAILWAY_PUBLIC_DOMAIN).
- Create an admin user. Two files, not one: Both are done by the entrypoint from
BLOJSOM_ADMIN_PASSWORD.authorization.properties:admin=<md5 hex>withuse-encrypted-passwords=trueanddigest-algorithm=MD5added toblog.properties(plaintext also works; the file format supports either).permissions.properties:admin=*. Without this you can log in but every admin page says "You do not have permission". The shipped file only grantsdefault=*, anddefaultis not a user in the shippedauthorization.properties.
- Listen on
$PORT. Tomcat'sserver.xmlhardcodes 8080; entrypointseds it. Railway also lets you pin the target port to 8080 on the domain, which is what I did, so this is belt-and-braces.
- Railway plumbing: project, empty service, volume at
/data, two env vars, generate a domain,railway upfrom this directory (25 MB upload; the Dockerfile is autodetected). First build+deploy about 60 seconds.
Dead ends hit
unzipis not in the Tomcat image. Usedjar xf(present, since the image has a JDK) to explode the WAR. Exploding rather than dropping the.warin was a choice: blojsom's admin plugins write back intoWEB-INF/default/*.propertiesat runtime, which only works on an exploded layout.ant wardeleteswar/WEB-INF/default/templatesafter jarring, so you cannot justCOPYthewar/directory out of the build stage; you must takedistro/blojsom.war.- Shipped
authorization.propertieshas no trailing newline. A naiveecho >> filegluesadmin=...onto the last comment line. Fixed insetprop. - The permissions file (step 6). Not mentioned anywhere obvious; found by grepping for the error string.
What does not work, or is degraded, and was left alone
- Weather plugin logs a 403 at boot: it fetches
http://www.nws.noaa.gov/data/current_obs/null.xml. Harmless, unconfigured plugin phoning a dead URL. - Ping URLs (
rpc.weblogs.com,rpc.pingomatic.com) on publish: pingomatic still exists, weblogs.com does not. Unchecked the "ping" box when posting; leaving it checked just adds a slow timeout to publishing. - Outbound email (comment notifications) points at
smtp-server=localhostinweb.xml. No SMTP in the container;blog-email-enabled=falseby default so nothing breaks. - Admin-UI edits to blog settings (name, description, plugins) land in
WEB-INF/default/*.propertiesinside the container image, not on the volume. They survive restarts but not redeploys. Posts, comments, and trackbacks are on the volume and survive both (verified by restart). Fixing this properly means pointingblojsom-configuration-base-directoryoff-WAR, which blojsom 2.x resolves relative to the servlet context, so it would be an actual change; skipped. - Security. This is 2006 code: MD5 passwords, no CSRF tokens, a comment form open to the internet, an XML-RPC endpoint (
/blojsom/xmlrpc/) and Atom API on by default, log4j 1.2.8 (not the log4shell branch, but unmaintained). Do not put anything you care about behind that admin password. Fine for a demo, not fine for a real blog without at least fronting it with something. - HTTPS. Railway terminates TLS at the edge; Tomcat sees plain HTTP. blojsom does not care because it builds URLs from
blog-base-url, which we set tohttps://.... If you ever set that tohttp://, browsers will block the mixed-content form posts.
Could a normal person do this in a weekend?
Yes, with these caveats about who "a normal person" is:
- Someone who has run Docker before and knows what a WAR is: an afternoon. The whole thing is one Dockerfile and a shell script; the only genuine research is "which JDK / which Tomcat", and both answers are one search away.
- Someone who has never touched Java: still a weekend, but a frustrating one. The failure modes (silent redirect to
localhost:8080on login, the permissions file, the templates directory being deleted after the WAR is built) produce no useful error messages. Expect to readweb.xml,blog.properties, and one Java file. - **Someone who wants to do it without Docker,** on a classic VPS: also fine, arguably closer to what the 2006 INSTALL doc assumed.
apt install openjdk-8-jdk tomcat9on Debian/Ubuntu,ant war, copy the WAR. Same config edits, done by hand.
What would push it past a weekend: wanting Tomcat 10/11 (jakarta migration), wanting a supported JDK (17/21 will run the compiled classes fine; it is only the compile step that needs 8, so you could build on 8 and run on 21 with Tomcat 9), or wanting blojsom 3.x (needs a real database and Hibernate 3.1 against a modern driver; that is where "as few changes as possible" stops being small).
Files
Dockerfile(repo dir, two-stage: JDK 8 + Ant build, Tomcat 9 + JDK 8 runtime)deploy/entrypoint.sh(env var to properties file shim; seeds an empty volume)deploy/seed-blog/general/hello-world.html(one entry so the first page is not blank)deploy/README.md(this file)
Nothing else in the tree was modified.
Run locally:
docker build -t blojsom228 . docker run -p 8080:8080 -e BLOJSOM_ADMIN_PASSWORD=changeme blojsom228 # http://localhost:8080/blojsom/blog/ admin: ?flavor=admin
Deploy (Railway, account already linked):
railway up --detach
Env vars the image understands: PORT, BLOJSOM_BASE_URL, BLOJSOM_BLOG_HOME, BLOJSOM_ADMIN_PASSWORD.
