Documentation
.env variables like SPEC2JIRA_API_KEY, and across this site.
Overview
The Spec2JIRA backend runs AI models that convert your Confluence specifications into structured Jira work items. It runs on your infrastructure — your data never leaves your network.
The Forge app in Confluence and Jira (installed from the Atlassian Marketplace as Spec2Tickets for Confluence and Jira) connects to your self-hosted backend over HTTPS. All AI inference happens on your hardware using the open-source Qwen 2.5 model.
Requirements
| Component | Requirement |
|---|---|
| OS | Ubuntu 24.04 LTS or 22.04 LTS (tested on GCP Deep Learning VM with CUDA) |
| Image | Deep Learning VM with CUDA (Ubuntu 24.04, CUDA 12.x, Python 3.12) |
| GPU | NVIDIA with ≥24 GB VRAM — tested: L4, A10, A100, RTX 4090 |
| RAM | 32 GB system RAM (16 GB minimum, 32 GB recommended) |
| Disk | SSD, 100 GB free (10 GB image + 25 GB models + Docker overhead) |
| Drivers | NVIDIA driver 550+ |
| Software | Docker 24+, Docker Compose v2, NVIDIA Container Toolkit |
| Network | Static IP or domain with HTTPS, reachable from Atlassian Cloud |
Using a Cloud GPU VM?
If you're using a cloud GPU image (e.g., GCP Deep Learning VM, AWS Deep Learning AMI), NVIDIA drivers, Docker, and the Container Toolkit are typically pre-installed. Verify with:
nvidia-smi # Should show your GPU
docker --version # Should show Docker 24+
# If docker is missing, install it:
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
newgrp docker
docker run --rm --gpus all nvidia/cuda:12.8.0-base-ubuntu22.04 nvidia-smi
If all three work, skip directly to Step 2. If docker returns "permission denied", run newgrp docker or log out and back in — your user needs to be in the docker group.
Step 0: NVIDIA Drivers
Skip if nvidia-smi already works.
# Check if drivers are already installed
nvidia-smi
# If not found, install:
sudo apt update
sudo apt install -y nvidia-driver-550
# Reboot required after driver install
sudo reboot
# After reboot, verify:
nvidia-smi
# Should show your GPU model and driver version
# Pin kernel and NVIDIA driver packages against unattended upgrades
sudo apt-mark hold linux-image-generic linux-headers-generic linux-image-gcp
sudo apt-mark hold nvidia-driver-550 nvidia-dkms-550
# When you want to update them deliberately:
# sudo apt-mark unhold <package>
# sudo apt update && sudo apt upgrade
# sudo dkms autoinstall # rebuild NVIDIA module against new kernel
Step 1: Docker + NVIDIA Container Toolkit
Skip if docker run --rm --gpus all ... already works.
Install Docker
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# Apply group change without logging out:
newgrp docker
Install NVIDIA Container Toolkit
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | \
sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
# Verify GPU access in Docker:
docker run --rm --gpus all nvidia/cuda:12.8.0-base-ubuntu22.04 nvidia-smi
# Should show the same GPU as bare-metal nvidia-smi
Step 2: Download Spec2JIRA
curl -fsSL https://spec2jira.com/releases/spec2jira-backend-2.2.0.tar.gz | tar xz
cd spec2jira-backend
This gives you:
| File | Purpose |
|---|---|
docker-compose.yml | Container configuration (pulls from Docker Hub) |
setup.sh | Downloads AI models from Hugging Face |
.env.example | Configuration template |
requirements-train.txt | Dependencies for optional fine-tuning |
INSTALL.md | Offline copy of this guide |
Step 3: Download AI Models
The backend uses two open-source Qwen 2.5 models (~25 GB total download). This is a one-time step.
chmod +x setup.sh train.sh
./setup.sh
The script will automatically install any missing dependencies (pip, huggingface_hub) before downloading.
What this downloads:
- Qwen 2.5 14B Instruct (GGUF, ~9 GB) — used for spec extraction and classification
- Qwen 2.5 7B Instruct (~15 GB) — base for optional fine-tuning
Models are saved to ./models/ and mounted into the Docker container.
./setup.sh — it picks up where it left off.
Step 4: Configure
cp .env.example .env
nano .env # Ctrl+O, Enter, Ctrl+X to save and exit
Required settings:
| Variable | Description | Example |
|---|---|---|
ATLASSIAN_SITE_URL | Your Atlassian site (without protocol) | yourcompany.atlassian.net |
ATLASSIAN_SERVICE_ACCOUNT_EMAIL | Service account email | spec2jira-svc@yourcompany.com |
ATLASSIAN_SERVICE_ACCOUNT_TOKEN | API token for the service account | ATATT3xF... |
JIRA_PROJECT_KEY | Default Jira project for created issues | PROJ |
SPEC2JIRA_API_KEY | Backend API key (see Step 4.1) | QCRmjrQPm2APyQ6o... |
Creating a service account (recommended)
- Create a dedicated Atlassian account (e.g.,
spec2jira-svc@yourcompany.com). - Grant it read access to the Confluence spaces containing your specs.
- Grant it write access to the target Jira project.
- Generate an API token at id.atlassian.com/manage-profile/security/api-tokens.
- Use this email and token in
.env.
Using a dedicated service account with minimal permissions is a security best practice — it limits the backend's access to only what it needs.
Step 4.1: Generate Backend API Key
Spec2Tickets requires a shared secret between your self-hosted backend and the Confluence app. This key authenticates the Forge app to your backend.
- Generate a random key:
openssl rand -base64 32 - Set it in your backend
.envfile:SPEC2JIRA_API_KEY=<paste-output-here> - In Step 8, you will paste the same key into the Forge app settings. Both values must match exactly.
SPEC2JIRA_API_KEY is missing or shorter than 16 characters. This is intentional — it prevents accidental production deploys with no authentication.
Step 5: Start
docker compose up -d
# Wait ~60 seconds for models to load into GPU memory, then verify:
curl -H "Authorization: Bearer <your-api-key>" http://localhost:8000/health
/health endpoint requires a valid API key. A bare curl http://localhost:8000/health will return 401 Unauthorized — this is expected behavior, not an error.
Watch startup progress:
docker compose logs -f --tail=50
Step 6: Expose via HTTPS
The backend must be reachable from Atlassian Cloud over HTTPS.
6.1 DNS record
Add an A record pointing to your VM's public IP:
Type: A
Name: api
Points to: <your VM public IP>
TTL: 3600
6.2 Install nginx and certbot
sudo apt install -y nginx certbot python3-certbot-nginx
# Remove the default site (prevents it from intercepting traffic)
sudo rm -f /etc/nginx/sites-enabled/default
6.3 Initial HTTP config
Create /etc/nginx/sites-enabled/spec2jira:
sudo nano /etc/nginx/sites-enabled/spec2jira
Paste:
server {
listen 80;
server_name api.yourcompany.com;
location / {
proxy_pass http://127.0.0.1:8000;
}
}
sudo nginx -t && sudo systemctl reload nginx
6.4 Get the SSL certificate
sudo certbot --nginx -d api.yourcompany.com
Certbot will ask three questions on first run:
- Email address — enter your email (used for renewal reminders and security notices).
- Terms of Service — type
Yto agree (required). - Share email with EFF — type
YorN(your preference, does not affect the certificate).
If certbot fails, make sure your DNS A record from 6.1 has propagated and that port 80 is reachable from the internet.
6.5 Final HTTPS config
Open the config file for editing:
sudo nano /etc/nginx/sites-enabled/spec2jira
Replace the contents with the full HTTPS configuration below. Replace every instance of api.yourcompany.com with your actual domain (certbot writes the SSL paths automatically; adjust to match your domain):
server {
listen 443 ssl;
server_name api.yourcompany.com;
ssl_certificate /etc/letsencrypt/live/api.yourcompany.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.yourcompany.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
location / {
proxy_pass http://localhost:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 1800s; # Pipeline runs can take up to 25 minutes
}
}
server {
if ($host = api.yourcompany.com) {
return 301 https://$host$request_uri;
}
listen 80;
server_name api.yourcompany.com;
return 404;
}
sudo nginx -t && sudo systemctl reload nginx
# Verify HTTPS works:
curl -H "Authorization: Bearer <your-api-key>" https://api.yourcompany.com/health
Step 7: Request Backend Whitelist Approval
During early access, each customer's backend domain is added to the Spec2Tickets Forge app allowlist. This is a one-time, ~5-minute step on our side.
Email support@spec2jira.com with:
- Your backend domain (e.g.,
api.yourcompany.com) - Your Atlassian site URL (e.g.,
yourcompany.atlassian.net) - Your name and company
We respond within one business day with a confirmation email. Once you receive confirmation, the Forge app installed on your Atlassian site can reach your backend.
Step 8: Connect the Forge App
- Install Spec2Tickets for Confluence and Jira from the Atlassian Marketplace.
- In Confluence, open any page. In the page toolbar, click the … (more) menu.
- Select Spec2Tickets for Confluence and Jira. An onboarding screen will appear with configuration instructions.
- Follow the onboarding instructions to open Apps → Spec2JIRA → Configure.
- In the configuration page, enter:
- Your backend URL (e.g.,
https://api.yourcompany.com) - Your
SPEC2JIRA_API_KEYfrom your backend.envfile - Your default Jira Project Key (e.g.,
PROJ)
- Your backend URL (e.g.,
- Click Save.
- Click Test Connection — you should see a green checkmark.
You're ready — open any Confluence specification page and click Generate Breakdown.
Commands Reference
| Command | Description |
|---|---|
docker compose up -d | Start backend |
docker compose down | Stop backend |
docker compose logs -f | View live logs |
docker compose restart | Restart after config change |
docker compose pull && docker compose up -d | Update to latest version |
Fine-Tuning (Optional)
Improve breakdown quality by training on your own specification data:
# 1. Install training dependencies (one-time, inside the container)
docker exec -it spec2jira-backend pip install -r /training/requirements-train.txt
# 2. Add training pairs to training/data/ (see training/README.md for format)
# 3. Run training (~10-15 minutes on L4)
docker exec -it spec2jira-backend python -m app.training.train_qwen
# 4. Update config to use fine-tuned model
# Edit .env: set PIPELINE_MODE=finetuned
# 5. Restart
docker compose restart
Troubleshooting
docker gives "permission denied"
Run newgrp docker or log out and back in. Verify your user is in the docker group: groups.
nvidia-container-toolkit install fails with dependency conflict
Common on cloud GPU VMs with pre-installed NVIDIA packages. Check if the toolkit is already working: docker run --rm --gpus all nvidia/cuda:12.8.0-base-ubuntu22.04 nvidia-smi. If it works, skip the toolkit install step.
nvidia-smi works on host but docker run --gpus all fails
NVIDIA Container Toolkit is not installed or Docker was not restarted. Run: sudo nvidia-ctk runtime configure --runtime=docker && sudo systemctl restart docker.
setup.sh fails with "externally-managed-environment" error (Ubuntu 24.04)
Ubuntu 24.04 ships Python 3.12, which enforces PEP 668 — system Python cannot have packages installed via pip without an explicit override. The setup.sh script's pip install huggingface_hub step is blocked with this error message. The workaround is a one-time --break-system-packages flag, then re-run setup.sh:
pip3 install huggingface_hub --break-system-packages
./setup.sh
This flag is safe in this context — huggingface_hub is used only by setup.sh to download AI models from Hugging Face; the backend itself runs containerized in Docker and does not depend on the host's system Python state. setup.sh will recognize the dependency as already installed and proceed directly to model download.
"No space left on device" during Docker pull
You need at least 100 GB free. Check with df -h /. Clean Docker cache: docker system prune -af.
"Out of memory" during generation
Ensure no other GPU processes are running: nvidia-smi. The pipeline needs ~18 GB VRAM peak during inference — 16 GB GPUs are not sufficient.
Backend refuses to start (ValidationError)
If the container exits immediately with a Pydantic ValidationError mentioning SPEC2JIRA_API_KEY, your API key is either missing from .env or shorter than 16 characters. Generate one with openssl rand -base64 32 and add it to your .env file. See Step 4.1.
Health check returns 401 Unauthorized
This is expected. The /health endpoint requires authentication. Use: curl -H "Authorization: Bearer <your-api-key>" http://localhost:8000/health. If you get 401 with a valid key, verify the key in .env matches exactly (no trailing spaces or newlines).
Backend starts but health check fails
Models may still be loading. Wait 2 minutes and retry. Check logs: docker compose logs -f. Verify models exist: ls -la models/base/.
Backend unreachable from Confluence
Verify HTTPS works from an external machine: curl -H "Authorization: Bearer <your-api-key>" https://api.yourcompany.com/health. Verify firewall allows inbound port 443. If behind a corporate firewall, ensure Atlassian Cloud IPs can reach your server.
"Test Connection" fails with 403 error
Your backend domain may not yet be whitelisted in the Spec2Tickets Forge app. See Step 7. If you've already received whitelist confirmation, verify that the backend URL in your Forge app configuration exactly matches the domain you submitted.
"Test Connection" fails with "Unauthorized"
The API key in Forge app settings does not match the SPEC2JIRA_API_KEY in your backend .env. Copy the exact value from .env and paste into the Backend API Key field in Forge settings. Both must match character-for-character.
Pipeline runs time out (502 Bad Gateway)
Increase proxy_read_timeout in nginx to at least 1800s. Large specifications (5000+ words) can take 20–25 minutes to process.
"Model file not found" on startup
Run ./setup.sh again — the model download may have been interrupted. Verify: ls -la models/base/Qwen2.5-14B-Instruct-GGUF/.
Need help?
Contact support@spec2jira.com.
Data Privacy
- All AI processing happens on your infrastructure.
- The backend uses customer-provided Atlassian API credentials to read Confluence pages and create Jira issues — these credentials are stored in your
.envfile only, never transmitted externally. - No data passes through Spec2JIRA servers (there are none — the architecture is fully self-hosted).
- Models run locally with no external API calls during inference.
- See our full privacy policy for details.