para: a multitenant backend for object persistence and retrieval
Multitenant backend server for building web and mobile apps rapidly. The backend for busy developers. (self-hosted or hosted)
At a glance
- What is it?
- Para is a Java server for storing and retrieving objects, and the design decision that shapes it is that the storage, the search index, the cache, and the way clients authenticate are all replaceable pieces. What you get is a JSON API that does not care whether the data lives in a document store, a wide column store, or a relational database, and that several applications can share without seeing each other's data.
- Who is it for?
- Para fits a developer who wants a working backend this afternoon rather than a data model this quarter, and a team that wants one server to hold several applications with separate tables, indexes, caches and credentials. It does not fit somebody who needs a specific database's advanced features, because the portability that is the point of the design also caps what you can rely on.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly Java, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What the project is
Para is a scalable, multitenant backend server and framework for object persistence and retrieval. It helps you build and prototype applications faster by taking care of backend operations. It can be a part of your JVM-based application, or it can be deployed as a standalone, multitenant API server with multiple applications and clients connecting to it.
That last distinction is the important one, because it is two products in one codebase. Embedded, it is a set of classes your own application calls, with the storage and search decisions made in your own configuration. Standalone, it is a server that several applications connect to over HTTP, each with its own data, and each with its own credentials.
The name is Bulgarian for steam, and the readme's gloss is that just as steam is used to power things, you can use Para to power a mobile or web application backend. It also links a comparison with other open source backend frameworks, which is unusual to find in a readme and tells you the project has been compared to the alternatives carefully enough to publish the comparison.
The project is Apache 2.0 licensed and is funded and supported by an independent, bootstrapped company, which is a slightly unusual arrangement and worth noting: the code is open, the support is a business, and the company is small enough that the two statements are related rather than independent.
There is hosting as well, with a free developer account and a paid tier that scales projects up and down and manages multiple applications, plus an admin console in the browser for managing users and objects, taking backups and editing permissions. And a separate open console project for the self-hosted case.
The release history shows the shape of a project that is still moving: three releases in the last few weeks of the year, one of them a patch, and a version number in the mid fifties that has been climbing steadily.
And one feature in the list is newer than everything else here, which is that the server speaks a protocol for AI agents to interact with your backend. That is the kind of feature that says where a backend framework is heading when the client may be a model rather than a person.
The feature list, read as a set of substitutions
The feature list is long, but it is easier to read as a list of the things you get to choose.
The storage choice is the first. The library is database-agnostic and is designed for scalable data stores, with document, wide column and other engines named as candidates. The search layer is the second, with full-text search through Lucene or Elasticsearch. The cache is the third, distributed or local, with a distributed in-memory one and a local one named. The authentication is the fourth, and it comes in two shapes: requests are signed with Amazon's signature algorithm, and client authentication is stateless with JSON Web Tokens.
So the picture is a server with four replaceable subsystems, and the readme's architecture drawing shows exactly that, with the interface layer on the left, the search and the persistence and the cache in the middle, and the data store at the bottom right, every one of them marked as any implementation. The clients at the bottom are the interesting part of that drawing: JavaScript, PHP, Java, C#, Android, iOS, and others, which is the statement that the server does not want you to be in one language.
On top of those substitutions there is a set of application-level features. Multitenancy is the one that gives the project its name in the description: each application has its own table, its own index, and its own cache. Webhooks with signed payloads let you react to changes. Security is flexible and built on a widely used framework, so directory services, federation, social login, and cross-site request protection are all available. There is a simple permission model for controlling what clients may do with resources, and a validation mechanism based on a standard for constraint validation, so the same rules apply wherever the object came from.
There is also fine-grained control over persistence, index and cache operations per object, which is the escape hatch for the case where a particular record should not be indexed or not be cached, and support for optimistic locking and transactions, though the readme is careful that the latter are implemented natively by each persistence implementation rather than by the framework.
That caveat is the one to remember. The portability is real, but so is the ceiling: what you get from a transaction depends on which store is underneath you.
Running it, and the configuration file
The quick start is five steps, and the second one is the one that matters.
You download the latest executable JAR, you create a configuration file in the same directory as the JAR, and you start the server with the configuration file pointed at on the command line. Then you install the command line client from the package manager, and you create a dedicated application for your project and save its access keys.
The configuration file being in the same directory as the JAR is a convention worth noting, because it means a deployment is a directory: the JAR, the configuration, and whatever the data directory holds. Moving the deployment means copying the directory, and the configuration is the only file anybody has to edit.
The command line client is a separate project, and the sequence in the readme is a setup that points at an endpoint, either a local one or the hosted one, followed by the creation of a new application with a display name.
# run setup and set endpoint to either 'http://localhost:8080' or 'https://paraio.com'
# the keys for the root app are inside application.conf
$ para-cli setup
$ para-cli new-app "myapp" --name "My App"The comment about the root application's keys living in the configuration file is the practical detail. The very first application is created by the server itself, and its credentials are in the configuration, and you use those to authenticate the creation of your own application, which then gets its own credentials. It is the same shape as most systems that hand out per-application keys, and the readme tells you where to look for the first pair.
After that there are three ways forward, and the readme lists them as alternatives rather than as steps. You can manage data through the web console in a browser, or you can integrate the server directly into your project with one of the client libraries, or you can just use the command line tool. For somebody prototyping, the console is enough; for somebody shipping, the client library is.
And there is a public endpoint that accepts form submissions with multipart encoding, which is the feature that makes the server usable from something that is not a JavaScript application. A plain HTML form posting to a public address is the oldest way to submit data, and a backend that only speaks signed JSON requests cannot accept it. This one can, and the readme treats that as a feature rather than as an afterthought.
The container, and the tag you should not use
The container section leads with a warning, and it is the kind of warning that saves an afternoon.
Tagged images are published under the maintainer's own name on the public registry, and the readme says it is highly recommended that you pull only release images, naming a specific version and a stable moving tag, because the moving latest tag can be broken or unstable.
That is an unusual thing to write about your own image, and it says something about the build pipeline: the tag that tracks the newest build is not necessarily the one you want in production, because a nightly build can fail in a way that does not fail the release pipeline. The recommendation is therefore to pin, and to use a named stable tag when you want something that moves.
The run command itself is short, and it shows the shape of a deployment.
$ touch application.conf && mkdir data
$ docker run -ti -p 8080:8080 --rm -v $(pwd)/data:/para/data \
-v $(pwd)/application.conf:/para/application.conf \
-e JAVA_OPTS="-Dconfig.file=/para/application.conf" erudikaltd/para:latest_stableTwo volumes are mounted: one for the data directory and one for the configuration file. The configuration is mounted rather than baked in, which means the same image runs against a development configuration and a production one, and the data is outside the container so that it survives it.
The single environment variable is the Java options string, and its default points at a directory of libraries to load from. That default is the interesting one, because it is the hook that the plugin mechanism uses: the server loads its persistence, search and cache implementations from a directory of JARs at run time, and mounting a directory of different JARs into that place is how you swap an implementation without building a new image.
That single environment variable is also what makes the plugin story work in a container, and it is why the next section is about composing images out of other images rather than about compiling anything.
Composing an image out of plugins
You can build a custom container with all the plugins and database drivers you need, and the readme gives the recipe using compose and a multi-stage build file.
The build file is short and readable, and it works by pulling several published images and copying JARs out of them.
ARG PARA_VERSION="0.0.0"
ARG SQL_DAO_VERSION="0.0.0"
FROM erudikaltd/para:v${PARA_VERSION} AS base
FROM erudikaltd/para-dao-sql:${SQL_DAO_VERSION} AS dao
FROM erudikaltd/para-search-lucene:${SEARCH_VERSION} AS search
FROM base AS final
COPY --from=dao /para/lib/*.jar /para/lib
COPY --from=search /para/lib/*.jar /para/lib
# EXAMPLE: Add a PostgreSQL JDBC Driver
ARG PG_JDBC_VERSION="0.0.0"
ADD https://jdbc.postgresql.org/download/postgresql-${PG_JDBC_VERSION}.jar /para/lib/The base stage is the server image at a pinned version. The other stages are images for a particular persistence implementation and a particular search implementation, and the only thing taken from them is the JARs. The final stage copies them all into the same directory the server loads its plugins from, and adds a database driver from a download.
So the plugin model is not a mechanism inside the application; it is a directory of JARs, and everything the framework calls pluggable is published as its own image with the same directory layout. Composing a server is then a matter of choosing images and copying files, which is a technique that needs no build of the server itself and no knowledge of its build system.
The version arguments at the top are the point of the whole arrangement. Each piece is versioned independently, so you can move the search implementation without moving the persistence one, and the build records the choices as arguments rather than as edits to a file. That is a small design decision with a large effect on how the thing is upgraded in a year.
The database driver is added with a download rather than copied from another stage, which is the simplest case: it is a single file with a version, and it goes in the same directory as everything else.
And the readme then shows the compose file that ties the image to a service definition, which is where the configuration file and the data directory come back into the picture as the two things a deployment has to provide.
The repository, and the build options
The top level of the repository is the standard layout for a multi-module Java project, and one detail in it explains how the plugin system is assembled.
There are three source modules: a core module, a server module, and a client module. The server is the standalone thing, the core holds the framework, and the client is the library an application uses to talk to a server it does not host. That last one matters because of the two-product split described at the start: the same server code serves the embedded case, and the client module is what an application that chose the standalone case depends on.
There is a build file at the root with a list of build options, and the default set is worth reading, because it names the optional pieces that get compiled in when you build everything in one artifact: a fat jar, the SQL persistence implementation, and the Lucene search implementation. Those are profiles rather than separate builds, so the executable JAR can carry a chosen set of implementations, and that is the same decision as the plugin directory, made at build time instead of at run time.
The container file is a two-stage build that starts from a Maven image with a Java runtime, copies the build file first so that the dependency layer is cached, and produces the runtime image. Caching the dependencies separately from the sources is the standard trick and it is worth noting that the build file is copied on its own for exactly that reason.
The rest of the top level is the operational surface. There is a deployment script, an installer script, a script for generating certificates, and a script for migrating the embedded database, which suggests the set of things a self-hosted operator actually has to do. There is a chart directory for Kubernetes deployment, and a properties file for the system's own settings.
And the governance files are the ones a security-conscious reader will look for first: a security policy, a code of conduct, a pull request template, and a contributing guide. For a project that holds application data and hands out credentials, a published route for reporting a vulnerability is part of the product, and its presence in the root rather than in a wiki is the right place for it.
Editorial conclusion
Para fits a developer who wants a working backend this afternoon rather than a data model this quarter, and a team that wants one server to hold several applications with separate tables, indexes, caches and credentials. It does not fit somebody who needs a specific database's advanced features, because the portability that is the point of the design also caps what you can rely on. Verify first which persistence and search implementations exist for the store you intend to use, because the design promises pluggability and the readme lists only some of the candidates; and check that the authentication scheme matches your clients, since the signed request mechanism and the token mechanism are different things and your client library has to speak the right one, and that you are using a tagged image rather than the moving tag, because the readme says the moving one can be broken.
Frequently asked questions
What is the Para server?
It is a scalable, multitenant backend server and framework for object persistence and retrieval, written in Java. It can be embedded in your own JVM application as a library, or deployed as a standalone API server that several applications and clients connect to, with each application having its own table, index, cache and credentials.
Which databases and search engines does Para support?
The design is database-agnostic, with scalable data stores such as document, wide column and other engines named as candidates, full-text search through Lucene or Elasticsearch, and either a distributed or a local object cache. The plugin mechanism is a directory of JARs loaded at run time, and the compose recipe shows a published image for each implementation so a server can be composed without rebuilding it.
How do I start Para?
Download the executable JAR, create a configuration file in the same directory, and start it with the configuration file pointed at on the command line. Then install the command line client, run its setup against your endpoint, and create a dedicated application for your project, keeping the access keys it returns.
Which Docker image should I use for Para?
Not the moving latest tag. The readme says to pull only release images, either a specific version or the stable moving tag, because the latest tag can be broken or unstable. The run mounts a directory for the data and a directory for the configuration file, and passes the configuration path through the Java options environment variable.
How do I add a database driver to a Para container?
By writing a small multi-stage build file that copies the JARs of the persistence and search implementations out of their published images into the plugin directory of the base image, and adding the driver file with a download, then building the result with compose. Each piece is versioned by its own build argument, so they can be moved independently.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/erudika-para)