# PublicCMS: one war file, three rendering modes and a data directory to get right

> PublicCMS is a Java CMS whose pages can be exported as static files, composed as server side includes or rendered live from FreeMarker templates, with SpringBoot, Hibernate and Hibernate Search underneath. The build, the container image and the compose topology each have an edge worth reading before you deploy one.

**sanluan/PublicCMS** — More than 2.7 million lines of code modification continuously iterated for 9 years to modernize java cms, easily supporting tens of millions of data, tens of millions of PV; Support static, server side includes; Currently has 0.0005% of the world's users (w3techs provided data), language support in Chinese, Japanese, English

- Repository: https://github.com/sanluan/PublicCMS
- Website: https://www.publiccms.com/
- Stars: 2,094 · Forks: 818
- Language: Java
- License: NOASSERTION
- Published: 2026-10-14 · Updated: 2026-10-14 · Language: en
- Canonical page: https://hysenlabs.com/projects/sanluan-publiccms

## FreeMarker templates, static export and SSI in one renderer

PublicCMS renders pages from templates rather than from a page builder alone. Its repository topics list freemarker, hibernate, hibernate-search, springboot and static-site-generator, and the project description names static pages, server side includes and dynamic page local static as supported output. One content tree can therefore end up as plain files on disk, as include fragments that a web server stitches together, or as pages rendered on each request, and a page that is too dynamic to render every time can be frozen into a local static copy.

URL rules are described as completely customisable, so the address of a page is a configuration choice rather than a byproduct of a routing table. Search runs through hibernate-search inside the same persistence stack that stores content, which removes a separate search cluster from the deployment. What the README does not give is any statement about how the rendering mode is chosen for a given page or a given site, and the deployment manual it defers to has no file of that name in the repository's top-level entries.

## Three JDK baselines and one MySQL floor decide the build

Three version lines run side by side and each sets a different Java floor: V4.0 needs jdk or jre 1.8 and later, V5 needs Java17 and later, V6 needs Java21 and later, and every line asks for mysql 5.5 and later. The default branch is V5, so a fresh clone already targets the Java 17 line rather than the oldest one the project still maintains.

Licensing differs across those lines: V4.0, V5 and V6 are free for individuals and paid for enterprises under a custom LICENSE, while V1.0, V2016, V2017, V2019 and V2021 sit under MIT. Three tags, V4.0.202608.a, V5.202608.a and V6.202608.a, carry the same 202608.a suffix and the same 2026-08-10 date, and the last push to the V5 default branch landed on 2026-10-06.

Two practical consequences follow. A company that deployed the default branch years ago cannot assume it still holds the same terms as the older branches it started from. And a team still on Java 8 cannot move to the default branch at all without a JDK upgrade first, which makes the branch choice a scheduling decision rather than a preference. The project's own description puts the codebase at more than 2.7 million lines of modification iterated over 9 years and claims 0.0005% of the world's users on w3techs data, a self-reported figure with no published measurement behind it.

## Maven builds a single war under the publiccms-parent directory

The repository keeps the source in publiccms-parent, documentation in doc and the writable data in a directory under data. The build runs from the parent directory with the bundled Maven wrapper, and the packaged artifact is one war file:

```bash
cd publiccms-parent
mvnw clean package
cd publiccms/target
java -jar -Dfile.encoding="UTF-8" -Dcms.filePath="../../../data/publiccms" publiccms.war
```

Starting the jar that way launches an embedded servlet container on port 8080 and points the data directory three levels up at the repository's own data folder. The relative path only resolves because the command changes into publiccms/target first, which is a detail worth keeping if you ever move the war somewhere else. Opening http://localhost:8080/ starts a guided database setup, and once the schema exists the management background is served from the relative path http://localhost:8080/admin/ rather than from a fixed domain. The data location is a startup argument and not a config file, so relocating an installation means passing the new path again on the next start.

## Startup properties move the port, context path and file path

Three system properties carry the runtime configuration, and the README gives the Windows and Linux forms of the same command:

```bash
java -jar -Dfile.encoding="UTF-8" -Dcms.port=8080 -Dcms.contextPath=/publiccms -Dcms.filePath="`pwd`/data/publiccms" publiccms.war
```

Here cms.port sets the listen port, cms.contextPath prefixes every URL including the admin background, and cms.filePath names the writable data directory the jar cannot infer from its own location. The Linux form interpolates the current working directory into the path, so it only produces a usable value when launched from the directory that contains data/. For the accepted values and defaults of each parameter the README sends readers to a deployment manual rather than spelling them out.

Tomcat deployment carries its own catch. The instructions say to rename publiccms.war.original to publiccms.war or to ROOT.war, where ROOT.war makes the context path the site root, and to move the file into the tomcat webapps directory while cms.filePath stays valid. A build from `mvnw clean package` produces publiccms.war directly and leaves no .original file behind, so that rename belongs to the downloadable package and not to a source build.

## The Docker image turns those properties into environment variables

The published image runs the war under eclipse-temurin:21-jdk with java -jar /opt/publiccms.war as its entrypoint, so the same system properties arrive through environment variables instead of typed switches:

```dockerfile
FROM eclipse-temurin:21-jdk
ADD publiccms-parent/publiccms/target/publiccms.war /opt/publiccms.war
ENV PORT=8080
ENV CONTEXTPATH=""
ENV INITPROTO=false
ENV FILEPATH="/data/publiccms"
VOLUME $FILEPATH
CMD ["-Dcms.port=$PORT -Dcms.initProto=$INITPROTO -Dcms.contextPath=$CONTEXTPATH -Dcms.filePath=$FILEPATH"]
ENTRYPOINT ["java","-jar","/opt/publiccms.war"]
```

One parameter appears here and nowhere in the manual commands: cms.initProto, switched off by default. The CMD line carries four names interpolated at container start, so PORT, CONTEXTPATH, INITPROTO and FILEPATH are the whole surface for tuning a container, and FILEPATH is declared as a volume so uploaded templates and generated pages sit outside the image once a host directory is mounted over it.

The gap sits in the build-your-own-image step. `docker build -t mypubliccms .` is documented as a two-line recipe, yet the Dockerfile copies publiccms-parent/publiccms/target/publiccms.war from the host, and that path exists only after the Maven build has produced it. A fresh clone that skips `mvnw clean package` fails at the ADD step during the image build, not at runtime, and the wording of the README does not mention the prerequisite.

## Compose runs nginx, the app and mysql as three containers

Starting with `docker compose up -d` brings up three services named publiccms, mysql and nginx, published on ports 8080, 3306 and 80, with every container pinned to TZ=Asia/Shanghai. The database credentials are written into the compose file rather than generated:

```yaml
- MYSQL_ROOT_PASSWORD=password!@#
- MYSQL_DATABASE=publiccms
- MYSQL_USER=publiccms
- MYSQL_PASSWORD=password!@#
```

One password serves both the root account and the publiccms user, and the README repeats the pair in prose as database publiccms with user/password publiccms/password!@#. Anyone who copies the file unedited starts a database reachable on host port 3306 with a password printed in the repository.

The topology matters more than the credentials. Both the app and nginx mount ./data/publiccms at the same container path, so nginx can serve generated pages straight off the shared volume while the application writes into it. nginx takes its site configuration from ./data/nginx-config mounted over /etc/nginx/conf.d, a host path the repository's top-level listing does not name, and the compose file pairs `version: "3.9"` with image: mysql:latest, which fixes no database version even though the stated floor is mysql 5.5 and later. Pinning the tag and rewriting the password are the first edits to make.

## Static export, SSI or live templates pick the hosting model

Three hosting shapes are available and the choice is visible in the repository files. Running `docker run -d -p 8080:8080 sanluan/publiccms` gives a single JVM answering on 8080 with no web server in front, which suits the live path where each request is rendered by the application. Adding nginx-cms from the compose file, sharing the same data volume and publishing port 80, suits the static path where PublicCMS writes files that nginx serves without touching the application. Server side includes sit between the two, with the web server composing fragments the application generated.

Against a plain static site generator the difference is that the same templates and the same content tree feed all three outputs rather than one, so a site can start dynamic and be frozen to static as traffic grows. Against a conventional CMS plugin model the trade-off runs the other way: the top-level listing carries no plugin or extension directory, so extending the system means changing code inside publiccms-parent and rebuilding the war rather than dropping a module into a folder. That is cheaper for a team that already owns its Java and far heavier for anyone who expected an installable extension.

## The repository ships history and security files the README leaves alone

The top-level entries include Update History.md, SECURITY.md and a doc directory, and the README mentions none of them beyond a deployment manual that is absent from the root listing. There is no documented rollback path: no note on returning to an earlier tag, no instruction for migrating a database between lines, and no statement about what an existing data directory does when the version under it changes. The demo credentials are published too, since the background demo at https://cms.publiccms.com/admin/ is offered with test/test as the demo account and password, which is reasonable for a demo site and wrong for anything else.

The interface demo at https://cms.publiccms.com/interface.html is the only pointer to a programmatic surface, and no endpoints, authentication scheme or rate limits are given for it anywhere in the README. Source mirrors sit on gitee.com/sanluan/PublicCMS and gitcode.com/sanluan/PublicCMS next to the GitHub repository, and a ready-to-run executable package is offered at https://www.publiccms.com/download.html, to be unpacked and started with startup.bat or startup.sh once java and mysql are present on the machine.

## Conclusion

PublicCMS suits teams that already run Java and MySQL and want page rendering they can move between static export, SSI and live templates without rebuilding the stack. It does not suit a team that needs a documented REST surface or a plugin catalogue, because the only pointer to an interface is a demo page. Before adopting, pin the mysql image tag, replace the password!@# written into docker-compose.yml, and confirm your JDK matches the line you build, since the image runs eclipse-temurin:21-jdk while the default V5 branch targets Java 17.

## FAQ

### Which Java version does PublicCMS need for each version line?

V4.0 requires jdk or jre 1.8 and later, V5 requires Java17 and later, and V6 requires Java21 and later, with mysql 5.5 and later for all of them. The repository's default branch is V5, so a clone without switching branches targets the Java 17 line.

### How do I start PublicCMS with docker compose and what database does it create?

Running `docker compose up -d` starts the publiccms, mysql and nginx services, with mysql exposed as container mysql-cms on port 3306 holding database publiccms and user publiccms with password!@#. nginx publishes port 80 and the application publishes port 8080, and both mount the same ./data/publiccms volume.

### Can I deploy PublicCMS into an existing Tomcat installation?

Yes, by renaming publiccms.war.original to publiccms.war or ROOT.war and moving it into the tomcat webapps directory, with the cms.filePath parameter still valid. Note that `mvnw clean package` builds publiccms.war directly and creates no .original file, so the rename applies to the downloadable package rather than to a source build.

### Where can I get a ready-made PublicCMS build without compiling the source?

The project offers a direct download of an executable program at https://www.publiccms.com/download.html. Unpack it locally with java and mysql already installed, then run startup.bat or startup.sh; further container parameters are documented on the sanluan/publiccms page at hub.docker.com.

### Does PublicCMS expose an API I can use for a headless frontend?

The README points at an interface demo page at https://cms.publiccms.com/interface.html and nothing more. No endpoints, authentication scheme or rate limits are documented, so treat any headless plan as unverified until you read the code under publiccms-parent.

### What are the default admin credentials for the PublicCMS demo?

The background demo at https://cms.publiccms.com/admin/ is published with test/test as the demo account and password. On your own installation the management background sits at the /admin/ relative path of whatever host and context path you started the war with.

## Sources

- [Issues](https://github.com/sanluan/PublicCMS/issues)
- [Project website](https://www.publiccms.com/)
- [README](https://github.com/sanluan/PublicCMS/blob/V5/README.md)
- [Releases](https://github.com/sanluan/PublicCMS/releases)
- [sanluan/PublicCMS on GitHub](https://github.com/sanluan/PublicCMS)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/sanluan-publiccms
