Installing Jira and Confluence with Docker: A Practical Guide
If you have worked in a software or technical team, you have probably come across Jira and Confluence. The two are often used together, but they solve different problems.
Jira is mainly used to manage projects, tasks, bugs, development workflows, and sprints. Confluence, on the other hand, provides a central place for documentation and team knowledge.
In this guide, we will first look at what Jira and Confluence are, then set up both applications using Docker and Docker Compose.
By the end, we will have two separate services running:
- Jira
- Confluence
Each application will also use its own PostgreSQL database rather than relying on an embedded database.
What Is Jira?
Jira is a project and issue-tracking platform developed by Atlassian.
It originally became popular as a bug-tracking system, but over time it evolved into a much broader project management platform. Today, it is widely used by development, DevOps, QA, product, and other technical teams.
Jira can be used to manage things such as:
- Tasks
- Bugs
- Stories
- Epics
- Sprints
- Releases
- Workflows
Suppose a development team is implementing a new feature called User Login with OTP.
A Story can be created in Jira, with separate tasks for backend development, frontend implementation, testing, security review, and deployment.
The work can then move through a workflow such as:
To Do
↓
In Progress
↓
Code Review
↓
Testing
↓
Done
This is only an example. Jira workflows are highly customizable, so organizations can define a process that matches the way their teams actually work.
What Is Confluence?
Confluence is Atlassian’s collaboration and documentation platform.
Technical teams usually generate a large amount of information that does not belong inside individual Jira issues.
For example:
- System architecture documentation
- Deployment guides
- API documentation
- Meeting notes
- Server runbooks
- Operational procedures
- Technical decisions
- Project documentation
- User guides
Confluence provides a central place to organize and maintain this information.
One useful aspect of running Jira and Confluence together is that project work and documentation can be connected.
For example, the technical design for a feature may live in Confluence while the implementation tasks are tracked in Jira.
This gives the team both sides of the project: the work being performed and the documentation explaining it.
Our Docker Architecture
For this installation, we will use Docker Compose.
The basic architecture looks like this:
Users
|
|
Reverse Proxy
/ \
/ \
Jira Confluence
| |
| |
PostgreSQL PostgreSQL
Jira and Confluence will each have their own PostgreSQL database.
Keeping the databases separate makes administration, troubleshooting, backup, and future migration easier.
Server Requirements
Jira and Confluence are not particularly lightweight applications. Both are Java-based, and Jira in particular can consume a reasonable amount of memory.
For a small test environment, you can start with limited resources.
For a more serious installation, a reasonable starting point would be:
CPU: 4 cores or more
RAM: 8 GB minimum
Recommended RAM: 16 GB or more
Disk: At least 50 GB
The actual requirements depend heavily on the number of users, projects, plugins, attachments, and overall workload.
Using SSD storage is also strongly recommended, especially for PostgreSQL and application data.
Installing Docker
The following example assumes an Ubuntu server.
First, update the package index and install the required packages:
sudo apt update
sudo apt install -y \
ca-certificates \
curl \
gnupg
Create the directory used for Docker’s repository key:
sudo install -m 0755 -d /etc/apt/keyrings
Download the Docker GPG key:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
| sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
Now add the official Docker repository:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
Update the package index again:
sudo apt update
Install Docker Engine and the Docker Compose plugin:
sudo apt install -y \
docker-ce \
docker-ce-cli \
containerd.io \
docker-buildx-plugin \
docker-compose-plugin
Check that Docker is installed:
docker --version
Then verify Docker Compose:
docker compose version
Creating the Project Directory
It is a good idea to keep the Atlassian deployment in its own directory.
Create one under /opt:
sudo mkdir -p /opt/atlassian
cd /opt/atlassian
Conceptually, our deployment will contain:
/opt/atlassian/
├── docker-compose.yml
├── Jira
├── Confluence
├── Jira PostgreSQL
└── Confluence PostgreSQL
We will use Docker volumes for persistent application and database storage.
Creating the Docker Compose File
Create the Compose file:
nano docker-compose.yml
Add the following configuration:
services:
jira-db:
image: postgres:16
container_name: jira-db
restart: unless-stopped
environment:
POSTGRES_DB: jiradb
POSTGRES_USER: jira
POSTGRES_PASSWORD: CHANGE_ME_JIRA_DB_PASSWORD
volumes:
- jira-db-data:/var/lib/postgresql/data
networks:
- atlassian
jira:
image: atlassian/jira-software:latest
container_name: jira
restart: unless-stopped
depends_on:
- jira-db
ports:
- "8080:8080"
volumes:
- jira-data:/var/atlassian/application-data/jira
networks:
- atlassian
confluence-db:
image: postgres:16
container_name: confluence-db
restart: unless-stopped
environment:
POSTGRES_DB: confluencedb
POSTGRES_USER: confluence
POSTGRES_PASSWORD: CHANGE_ME_CONFLUENCE_DB_PASSWORD
volumes:
- confluence-db-data:/var/lib/postgresql/data
networks:
- atlassian
confluence:
image: atlassian/confluence:latest
container_name: confluence
restart: unless-stopped
depends_on:
- confluence-db
ports:
- "8090:8090"
volumes:
- confluence-data:/var/atlassian/application-data/confluence
networks:
- atlassian
volumes:
jira-data:
jira-db-data:
confluence-data:
confluence-db-data:
networks:
atlassian:
driver: bridge
Before starting anything, replace:
CHANGE_ME_JIRA_DB_PASSWORD
CHANGE_ME_CONFLUENCE_DB_PASSWORD
with strong passwords.
Hardcoded passwords like these are acceptable for demonstrating the setup, but they should not be used as-is in a production deployment.
Starting Jira and Confluence
Start all services in the background:
docker compose up -d
Check their status:
docker compose ps
We should now have four containers:
jira
jira-db
confluence
confluence-db
You can also check them directly with:
docker ps
Do not worry if Jira and Confluence are not immediately available after the containers start.
Both applications may take some time during their initial startup.
Checking Jira Logs
The first place I normally check after starting Jira is its container log.
Run:
docker compose logs -f jira
Alternatively:
docker logs -f jira
Once Jira has finished starting, it should be available on port 8080.
Open:
http://SERVER-IP:8080
in your browser.
Configuring the Jira Database
During the initial Jira setup, select PostgreSQL as the database.
One common mistake here is entering localhost as the database hostname.
Remember that Jira is running inside its own container.
From inside that container:
localhost
refers to the Jira container itself, not the PostgreSQL container.
Since both containers are connected to the same Docker network, Docker provides internal DNS resolution using the service name.
Use:
Database Type:
PostgreSQL
Hostname:
jira-db
Port:
5432
Database:
jiradb
Username:
jira
Password:
The password configured in docker-compose.yml
The connection therefore looks like:
Jira
|
|
jira-db:5432
|
|
PostgreSQL
There is no need to expose PostgreSQL publicly just so Jira can reach it.
Setting Up Confluence
Confluence is exposed on port 8090.
Open:
http://SERVER-IP:8090
During the initial setup, select PostgreSQL and enter:
Hostname:
confluence-db
Port:
5432
Database:
confluencedb
Username:
confluence
Password:
The password configured in docker-compose.yml
As with Jira, do not use localhost as the PostgreSQL hostname.
The Confluence container can reach PostgreSQL through the shared Docker network using the confluence-db service name.
Why Do We Need Docker Volumes?
One of the most important parts of the Compose configuration is persistent storage.
For example:
volumes:
- jira-data:/var/atlassian/application-data/jira
Containers should generally be treated as replaceable.
You may remove a container today and recreate it tomorrow from the same image.
Application data should therefore not depend on the lifetime of that individual container.
Docker volumes solve this problem.
The same principle applies to PostgreSQL:
volumes:
- jira-db-data:/var/lib/postgresql/data
If the PostgreSQL container needs to be recreated, the database remains stored in the volume.
You can list existing Docker volumes with:
docker volume ls
Monitoring Resource Usage
Because Jira and Confluence are Java applications, memory usage is something worth watching from the beginning.
A quick way to see container resource consumption is:
docker stats
You will see output similar to:
CONTAINER CPU MEMORY
jira ... ...
confluence ... ...
jira-db ... ...
confluence-db ... ...
You can also check the server’s available memory:
free -h
and disk usage:
df -h
If the host does not have enough RAM, running Jira and Confluence together may result in poor performance or, in more severe cases, processes being terminated by the Linux OOM killer.
Troubleshooting with Container Logs
When something goes wrong, logs are usually the best place to start.
Check the latest Jira logs:
docker logs jira --tail 200
For Confluence:
docker logs confluence --tail 200
For the Jira database:
docker logs jira-db --tail 200
And for the Confluence database:
docker logs confluence-db --tail 200
To follow logs in real time:
docker logs -f jira
or:
docker logs -f confluence
Database connection errors, permission problems, memory issues, and startup failures will usually leave useful information here.
Testing PostgreSQL
If Jira reports a database connection error, it is worth checking PostgreSQL independently before changing the Jira configuration.
Connect directly to the Jira database:
docker exec -it jira-db \
psql -U jira -d jiradb
If the connection works, you should reach the PostgreSQL prompt:
jiradb=#
Exit with:
\q
The same test can be performed for Confluence:
docker exec -it confluence-db \
psql -U confluence -d confluencedb
This simple test helps separate database problems from application configuration problems.
Restarting the Services
Restart Jira:
docker compose restart jira
Restart Confluence:
docker compose restart confluence
To stop the entire stack:
docker compose down
Start it again with:
docker compose up -d
A normal docker compose down does not remove the named volumes defined in the Compose file.
Be much more careful with:
docker compose down -v
The -v option removes volumes associated with the Compose project.
On a production system, deleting the wrong volume can mean deleting application or database data.
Putting Nginx in Front of Jira and Confluence
Accessing the applications using URLs such as:
http://server-ip:8080
is fine for testing, but it is not how I would normally expose them in production.
It is cleaner to use separate subdomains, for example:
jira.example.com
and:
confluence.example.com
The architecture becomes:
Internet
|
|
HTTPS
|
Nginx
/ \
/ \
Jira Confluence
8080 8090
A simple Nginx configuration for Jira could look like:
server {
listen 80;
server_name jira.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
For Confluence:
server {
listen 80;
server_name confluence.example.com;
location / {
proxy_pass http://127.0.0.1:8090;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Check the Nginx configuration:
sudo nginx -t
If everything is valid:
sudo systemctl reload nginx
For a real deployment, HTTPS should also be enabled using a valid TLS certificate.
Backing Up PostgreSQL
A Jira or Confluence installation should not be considered production-ready until there is a proper backup strategy.
For the Jira database, we can use pg_dump:
docker exec jira-db \
pg_dump -U jira jiradb \
> jira-backup.sql
For Confluence:
docker exec confluence-db \
pg_dump -U confluence confluencedb \
> confluence-backup.sql
This gives us:
jira-backup.sql
confluence-backup.sql
Database backups are only one part of the picture, though.
The Jira and Confluence application data also needs to be included in the backup strategy.
For a production environment, backups should ideally:
- Run automatically
- Include timestamps
- Be copied to another storage location
- Have an appropriate retention policy
- Be monitored for failures
- Be periodically restored and tested
The last point is particularly important.
A backup that has never been successfully restored is not something I would rely on during an actual incident.
Production Considerations
Getting the containers running is the easy part.
If Jira and Confluence are going to be used by an actual organization, there are several other areas that deserve attention.
One is image versioning.
Our example uses:
image: atlassian/jira-software:latest
That is convenient for a lab, but I would avoid latest in production.
A better approach is to pin the deployment to a specific application version that has already been tested.
The same principle applies to Confluence and PostgreSQL.
Another consideration is secret management.
Database passwords should not simply live in a Compose file that is committed to a Git repository.
At minimum, sensitive configuration should be separated from the main Compose configuration and protected appropriately.
For larger environments, a dedicated secrets management solution may make more sense.
Monitoring is another area that should not be left until something breaks.
At a minimum, I would keep an eye on:
CPU Usage
Memory Usage
Disk Usage
Container Health
PostgreSQL Status
Application Logs
Backup Status
Disk usage deserves particular attention.
A full filesystem can quickly turn into a PostgreSQL problem, an application problem, or both.
Upgrading Jira and Confluence
Upgrades should be treated as controlled changes rather than simply pulling a new image and hoping for the best.
Before upgrading, take a backup.
Then review the release notes and compatibility requirements for the target version.
Once the target version has been selected and configured in the Compose file, pull the required images:
docker compose pull
Then recreate the services:
docker compose up -d
Watch the application logs during startup:
docker compose logs -f jira
and:
docker compose logs -f confluence
For important environments, upgrades should ideally be tested in a staging environment first.
This becomes even more important when the installation contains third-party apps or plugins.
Useful Docker Commands
Check service status:
docker compose ps
View logs:
docker compose logs
Follow Jira logs:
docker compose logs -f jira
Restart Jira:
docker compose restart jira
Monitor container resources:
docker stats
List Docker volumes:
docker volume ls
List Docker networks:
docker network ls
Open a shell inside Jira:
docker exec -it jira bash
Open a shell inside Confluence:
docker exec -it confluence bash
You can also verify that Jira can resolve the PostgreSQL container:
docker exec -it jira getent hosts jira-db
If this returns the internal IP address of jira-db, Docker’s internal DNS resolution is working.
For crack
Add agent to env
docker cp atlassian-agent.jar confluence:/opt/atlassian/confluence/
docker cp atlassian-agent.jar jira:/opt/atlassian/jira/
echo ‘export CATALINA_OPTS=”-javaagent:/opt/atlassian/confluence/atlassian-agent.jar ${CATALINA_OPTS}”‘ >> /opt/atlassian/confluence/bin/setenv.sh
echo ‘export CATALINA_OPTS=”-javaagent:/opt/atlassian/jira/atlassian-agent.jar ${CATALINA_OPTS}”‘ >> /opt/atlassian/jira/bin/setenv.sh
you can use the same file to generate license code like below for any product or plugin you want
java -jar atlassian-agent.jar –help
restart jira or conf container
products
java -jar atlassian-agent.jar -d -m [Email] -n BAT -p [conf or jira] -o http://[serverip-or-domain] -s [serverId]
Final Thoughts
Jira and Confluence solve different but closely related problems.
Jira handles the operational side of project work: issues, tasks, bugs, stories, sprints, releases, and workflows.
Confluence handles the knowledge around that work: architecture documents, technical decisions, meeting notes, runbooks, procedures, and other documentation.
Docker makes the deployment easier to reproduce and maintain because the application, database, storage, and network configuration are clearly defined instead of being scattered across the host operating system.
Still, seeing the Jira login page load successfully does not mean the deployment is finished.
For a production environment, the following areas should be part of the design from the beginning:
Persistent Storage
Database Backups
Application Data Backups
HTTPS
Reverse Proxy
Monitoring
Resource Management
Version Pinning
Secret Management
Upgrade Strategy
Restore Testing
The Docker Compose stack in this guide is therefore best treated as a solid starting point. Once the basic deployment is working, it can be hardened and extended based on the number of users, availability requirements, security requirements, and the way Jira and Confluence will actually be used inside the organization.