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 into war/WEB-INF/lib). - No database. Entries are files on disk under
blog-home; comments, trackbacks, and metadata are sibling dot-directories. Config is .properties files under WEB-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/ (the tags/ directory stops at 2.19; later releases are under branches/). Trunk is 3.x, a different animal (Spring + Hibernate + SQL).
- Pick a JDK that still compiles
-source 1.4. JDK 9+ removed it (javac 9 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+ is jakarta.* 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 in webapps/, done. The web.xml is DTD-era 2.3 and Tomcat 9 accepts it without complaint.
- Give it somewhere to write.
blog.properties ships with blog-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) from blog-base-url / blog-url in blog.properties. Wrong value = admin login posts to localhost:8080 and fails silently. deploy/entrypoint.sh rewrites these at boot from BLOJSOM_BASE_URL (falls back to Railway's RAILWAY_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> with use-encrypted-passwords=true and digest-algorithm=MD5 added to blog.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 grants default=*, and default is not a user in the shipped authorization.properties.
- Listen on
$PORT. Tomcat's server.xml hardcodes 8080; entrypoint seds 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 up from this directory (25 MB upload; the Dockerfile is autodetected). First build+deploy about 60 seconds.
Dead ends hit
unzip is not in the Tomcat image. Used jar xf (present, since the image has a JDK) to explode the WAR. Exploding rather than dropping the .war in was a choice: blojsom's admin plugins write back into WEB-INF/default/*.properties at runtime, which only works on an exploded layout.ant war deletes war/WEB-INF/default/templates after jarring, so you cannot just COPY the war/ directory out of the build stage; you must take distro/blojsom.war.- Shipped
authorization.properties has no trailing newline. A naive echo >> file glues admin=... onto the last comment line. Fixed in setprop. - 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=localhost in web.xml. No SMTP in the container; blog-email-enabled=false by default so nothing breaks. - Admin-UI edits to blog settings (name, description, plugins) land in
WEB-INF/default/*.properties inside 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 pointing blojsom-configuration-base-directory off-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 to https://.... If you ever set that to http://, 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:8080 on login, the permissions file, the templates directory being deleted after the WAR is built) produce no useful error messages. Expect to read web.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 tomcat9 on 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.