This guide covers deploying Source Code Portal in production environments.
- Deployment Options
- System Requirements
- JAR Deployment
- Kubernetes Deployment
- Configuration
- Security
- SSL/TLS Configuration
- Production Checklist
- Best Practices
Source Code Portal supports multiple deployment options:
| Method | Best For | Complexity | High Availability |
|---|---|---|---|
| JAR | Single server, simple setups | Low | No |
| Docker | Containerized environments | Medium | With orchestration |
| Kubernetes | Production, cloud-native | High | Yes |
- Java: OpenJDK 21 LTS or Eclipse Temurin 21
- Memory: 512MB RAM (1GB recommended)
- CPU: 1 core (2 cores recommended)
- Disk: 500MB for application + 1GB for logs and cache
- Network: Internet access for GitHub API
- Java: Eclipse Temurin 21 (LTS)
- Memory: 2GB RAM
- CPU: 2 cores
- Disk: 5GB (2GB for application, 3GB for logs)
- Network: Dedicated outbound connection to GitHub API
- GitHub API:
https://api.github.com- Rate limits: 5000 requests/hour (authenticated)
- Ensure firewall allows HTTPS to GitHub
- Jenkins: For build status badges
- Snyk: For security test badges
- Shields.io: For custom badges
# Build production JAR
mvn clean package -DskipTests
# JAR location
ls target/source-code-portal-*.jar# Run directly
java -jar target/source-code-portal-*.jar
# Run with profile
java -jar target/source-code-portal-*.jar --spring.profiles.active=prod
# Run with JVM options
java -Xmx2g -Xms1g -jar target/source-code-portal-*.jarjava \
-Xmx2g \
-Xms1g \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-Dspring.profiles.active=prod \
-Dserver.port=9090 \
-jar target/source-code-portal-*.jarCreate /etc/systemd/system/sourcecodeportal.service:
[Unit]
Description=Source Code Portal
After=network.target
[Service]
Type=simple
User=scp
WorkingDirectory=/opt/sourcecodeportal
ExecStart=/usr/bin/java \
-Xmx2g \
-Xms1g \
-XX:+UseG1GC \
-Dspring.profiles.active=prod \
-jar /opt/sourcecodeportal/source-code-portal.jar
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.targetEnable and start:
# Create user
sudo useradd -r -s /bin/false scp
# Create directory
sudo mkdir -p /opt/sourcecodeportal
sudo cp target/source-code-portal-*.jar /opt/sourcecodeportal/source-code-portal.jar
sudo chown -R scp:scp /opt/sourcecodeportal
# Install service
sudo systemctl daemon-reload
sudo systemctl enable sourcecodeportal
sudo systemctl start sourcecodeportal
# Check status
sudo systemctl status sourcecodeportal
# View logs
sudo journalctl -u sourcecodeportal -fCreate deployment.yaml:
apiVersion: v1
kind: Namespace
metadata:
name: sourcecodeportal
---
apiVersion: v1
kind: ConfigMap
metadata:
name: scp-config
namespace: sourcecodeportal
data:
application.yml: |
spring:
profiles:
active: prod
server:
port: 9090
github:
organization: YourOrg
---
apiVersion: v1
kind: Secret
metadata:
name: scp-secrets
namespace: sourcecodeportal
type: Opaque
stringData:
github-client-id: YOUR_CLIENT_ID
github-client-secret: YOUR_CLIENT_SECRET
github-access-token: ghp_YOUR_ACCESS_TOKEN
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: sourcecodeportal
namespace: sourcecodeportal
spec:
replicas: 2
selector:
matchLabels:
app: sourcecodeportal
template:
metadata:
labels:
app: sourcecodeportal
spec:
containers:
- name: sourcecodeportal
image: cantara/sourcecodeportal:0.10.17
ports:
- containerPort: 9090
name: http
env:
- name: SPRING_PROFILES_ACTIVE
value: "prod"
- name: github.oauth2.client.clientId
valueFrom:
secretKeyRef:
name: scp-secrets
key: github-client-id
- name: github.oauth2.client.clientSecret
valueFrom:
secretKeyRef:
name: scp-secrets
key: github-client-secret
- name: github.client.accessToken
valueFrom:
secretKeyRef:
name: scp-secrets
key: github-access-token
volumeMounts:
- name: config
mountPath: /home/sourcecodeportal/config_override
readOnly: true
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "2000m"
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 9090
initialDelaySeconds: 60
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 9090
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
volumes:
- name: config
configMap:
name: scp-config
---
apiVersion: v1
kind: Service
metadata:
name: sourcecodeportal
namespace: sourcecodeportal
spec:
selector:
app: sourcecodeportal
ports:
- port: 80
targetPort: 9090
name: http
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: sourcecodeportal
namespace: sourcecodeportal
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- scp.example.com
secretName: scp-tls
rules:
- host: scp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: sourcecodeportal
port:
number: 80Deploy:
# Apply configuration
kubectl apply -f deployment.yaml
# Check deployment
kubectl get pods -n sourcecodeportal
kubectl get svc -n sourcecodeportal
kubectl get ingress -n sourcecodeportal
# View logs
kubectl logs -f deployment/sourcecodeportal -n sourcecodeportal
# Scale deployment
kubectl scale deployment/sourcecodeportal --replicas=3 -n sourcecodeportalSpring Boot Actuator provides health endpoints for Kubernetes probes:
- Liveness:
/actuator/health/liveness- Is the application running? - Readiness:
/actuator/health/readiness- Can the application accept traffic?
Configure in application.yml:
management:
endpoint:
health:
probes:
enabled: true
health:
livenessState:
enabled: true
readinessState:
enabled: trueConfiguration is loaded in this order (later overrides earlier):
src/main/resources/application-defaults.properties(built-in defaults)application.properties(custom overrides)security.properties(credentials)application_override.properties(final overrides)- Environment variables with
SCP_prefix - System properties (
-Dflags)
All configuration properties can be set via environment variables:
# GitHub configuration
export SCP_GITHUB_ACCESS_TOKEN=ghp_your_token
export SCP_GITHUB_ORGANIZATION=YourOrg
# Server configuration
export SCP_SERVER_PORT=9090
# Cache configuration
export SCP_CACHE_TTL=30
# Run application
java -jar source-code-portal.jarCreate application-prod.yml:
spring:
profiles:
active: prod
threads:
virtual:
enabled: true # Enable virtual threads for better I/O performance
server:
port: 9090
shutdown: graceful
undertow:
threads:
io: 4
worker: 20
github:
organization: ${GITHUB_ORGANIZATION:Cantara}
access-token: ${GITHUB_ACCESS_TOKEN:}
repository:
visibility: all # all, public, or private
oauth2:
client:
clientId: ${GITHUB_CLIENT_ID:}
clientSecret: ${GITHUB_CLIENT_SECRET:}
cache:
ttl: 30 # minutes
max-size: 10000
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: when-authorized
probes:
enabled: true
metrics:
export:
prometheus:
enabled: true
logging:
level:
no.cantara: INFO
org.springframework: WARN
file:
name: logs/application.log
logback:
rollingpolicy:
max-file-size: 10MB
max-history: 7Run with production profile:
java -jar source-code-portal.jar --spring.profiles.active=prodGenerate token at: https://github.com/settings/tokens
Required scopes:
repo(full repository access)read:org(read organization data)read:user(read user profile)
Configure:
export SCP_GITHUB_ACCESS_TOKEN=ghp_your_token- Create OAuth app: https://github.com/settings/developers
- Set callback URL:
http://your-server:9090/oauth/callback - Configure:
export SCP_GITHUB_CLIENT_ID=your_client_id
export SCP_GITHUB_CLIENT_SECRET=your_client_secretSecure webhook endpoint with secret:
export SCP_GITHUB_WEBHOOK_SECRET=your_webhook_secretConfigure in GitHub webhook settings:
- URL:
https://your-server.com/github/webhook - Content type:
application/json - Secret: Same as
SCP_GITHUB_WEBHOOK_SECRET
# Load secrets from file
source /etc/scp/secrets.env
java -jar source-code-portal.jar# Create secret
kubectl create secret generic scp-secrets \
--from-literal=github-access-token=ghp_token \
-n sourcecodeportal
# Reference in deployment
# (See Kubernetes deployment section)# Fetch secrets from Vault
export SCP_GITHUB_ACCESS_TOKEN=$(vault kv get -field=token secret/scp/github)
java -jar source-code-portal.jarUse Nginx or similar as SSL termination:
server {
listen 443 ssl http2;
server_name scp.example.com;
ssl_certificate /etc/ssl/certs/scp.crt;
ssl_certificate_key /etc/ssl/private/scp.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
proxy_pass http://localhost:9090;
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;
}
}Configure in application-prod.yml:
server:
port: 8443
ssl:
enabled: true
key-store: classpath:keystore.jks
key-store-password: changeit
key-store-type: JKS
key-alias: tomcatGenerate keystore:
keytool -genkeypair \
-alias tomcat \
-keyalg RSA \
-keysize 2048 \
-storetype JKS \
-keystore keystore.jks \
-validity 3650 \
-storepass changeitBefore deploying to production:
- GitHub OAuth credentials configured
- GitHub access token generated
- Repository groups defined in
config.json - Environment variables set
- SSL/TLS certificates configured
- Webhook secret configured
- Secrets stored securely (not in code)
- GitHub token has minimum required scopes
- Firewall rules configured
- HTTPS enabled
- Webhook endpoint secured
- Health checks configured
- Prometheus metrics enabled
- Log aggregation configured
- Alert thresholds defined
- On-call rotation established
- Resource limits set (CPU, memory)
- Cache TTL configured appropriately
- Virtual threads enabled
- Connection pooling configured
- Rate limiting implemented
- Configuration files backed up
- Disaster recovery plan documented
- Rollback procedure tested
- Database backups scheduled (if using DB)
- Load testing completed
- Failover testing completed
- Health check endpoints tested
- Webhook delivery tested
- Rate limit handling tested
- Run multiple replicas (Kubernetes:
replicas: 3) - Use health checks for automatic restart
- Configure graceful shutdown (
server.shutdown: graceful) - Implement circuit breakers (already done via Resilience4j)
- Use load balancer for traffic distribution
- Enable virtual threads for better I/O performance
- Configure cache appropriately (TTL, max size)
- Set JVM heap size based on memory available
- Use G1GC for better garbage collection
- Monitor thread pool usage via actuator
- Enable Spring Boot Actuator
- Export metrics to Prometheus
- Set up Grafana dashboards
- Configure log aggregation (ELK, Splunk)
- Set up alerts for critical issues
- Use minimal container images (Alpine-based)
- Run as non-root user
- Scan images for vulnerabilities (Trivy, Snyk)
- Keep dependencies updated
- Implement rate limiting on public endpoints
- Right-size resources (don't over-provision)
- Use caching effectively to reduce API calls
- Implement request batching where possible
- Monitor GitHub API usage to avoid rate limits
- Use spot instances for non-critical environments
- Docker Guide - Container deployment
- Monitoring Guide - Set up monitoring
- Troubleshooting Guide - Common issues