1. Install the database
Choose one.
# PostgreSQL
sudo apt install -y postgresql
sudo -u postgres psql -c "CREATE USER appuser WITH PASSWORD 'change-me';"
sudo -u postgres psql -c "CREATE DATABASE appdb OWNER appuser;"
# MariaDB (MySQL-compatible)
sudo apt install -y mariadb-server
sudo mysql -e "CREATE DATABASE appdb CHARACTER SET utf8mb4;"
sudo mysql -e "CREATE USER 'appuser'@'localhost' IDENTIFIED BY 'change-me';"
sudo mysql -e "GRANT ALL ON appdb.* TO 'appuser'@'localhost'; FLUSH PRIVILEGES;"
Generate a real password with openssl rand -hex 16. Both packages listen on localhost by default. Confirm with sudo ss -ltnp | grep -E '5432|3306': the address must be 127.0.0.1. Never open a database port to the internet.
2. Add the JDBC driver to Tomcat
Download the driver jar for your database from Maven Central into Tomcat's shared lib folder. Look up the current version number first.
cd /opt/tomcat/lib
# PostgreSQL
sudo curl -fSLO https://repo1.maven.org/maven2/org/postgresql/postgresql/42.7.4/postgresql-42.7.4.jar
# MariaDB
sudo curl -fSLO https://repo1.maven.org/maven2/org/mariadb/jdbc/mariadb-java-client/3.5.1/mariadb-java-client-3.5.1.jar
sudo chown tomcat:tomcat *.jar
A driver in lib is required for a JNDI DataSource defined by Tomcat. If your application bundles its own pool and driver in WEB-INF/lib, skip this and configure the pool inside the application.
3. Define the DataSource
For one application, put it in META-INF/context.xml inside the WAR, or in /opt/tomcat/conf/Catalina/localhost/ROOT.xml on the server, which keeps the password out of the WAR:
<Context>
<Resource name="jdbc/appdb" auth="Container" type="javax.sql.DataSource"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
driverClassName="org.postgresql.Driver"
url="jdbc:postgresql://127.0.0.1:5432/appdb"
username="appuser" password="change-me"
maxActive="20" maxIdle="5" minIdle="2" initialSize="2"
maxWait="10000"
testOnBorrow="true" validationQuery="SELECT 1"
removeAbandoned="true" removeAbandonedTimeout="120" />
</Context>
For MariaDB use org.mariadb.jdbc.Driver and jdbc:mariadb://127.0.0.1:3306/appdb. Protect the file: sudo chmod 640 ROOT.xml; sudo chown tomcat:tomcat ROOT.xml.
4. Look it up from the application
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/appdb");
try (Connection c = ds.getConnection()) {
// use the connection
}
With Spring, point spring.datasource.jndi-name to java:comp/env/jdbc/appdb. Always close connections: unclosed ones are the usual reason for the pool running dry. removeAbandoned above is a safety net, not a fix.
5. Restart and test
sudo systemctl restart tomcat
sudo tail -n 50 /opt/tomcat/logs/catalina.out
Sizing on a small server
- Every connection costs memory in the database too. A pool of 10 to 20 is plenty for most applications on a 4 GB server. A large pool does not make queries faster.
- With a database on the same machine, leave about 15% of RAM to it for buffers. See how much RAM a Java app needs.
- Take a daily dump and copy it off the server: backup guide.
Plans with a database on the server are described on Java hosting with a database.
Last updated October 29, 2026.