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.