Deployment Guide¶
This guide covers deploying ICOSYS services and the React frontend to test and production environments.
Environments¶
| Environment | Profile | Purpose |
|---|---|---|
| Development | dev |
Local workstation, all defaults |
| Test | test |
Staging server, restricted CORS |
| Production | prod |
Live system, full security |
Each Spring Boot service selects its profile at startup:
Build Artifacts¶
Backend — Executable JARs¶
# Shared libraries (if not already installed)
cd ICOM-Api-4.0 && mvn clean install -DskipTests && cd ..
cd ICOM-Services-Api-1.0 && mvn clean install -DskipTests && cd ..
cd Icglb-Entities-1.0 && mvn clean install -DskipTests && cd ..
cd Icglb-Entities-Dto-1.0 && mvn clean install -DskipTests && cd ..
cd ICOM-Micro-Services-Api-1.0 && mvn clean install -DskipTests && cd ..
# Service JARs
cd Icglb-Services-1.0 && mvn clean package -DskipTests && cd ..
cd ICoSys-BPM/ICoSys-BPM-Services-1.0 && mvn clean package -DskipTests && cd ../..
cd ICoSys-DMS-Service-1.0/ICoSys-DMS-Service-1.0 && mvn clean package -DskipTests && cd ../..
Output JARs:
| Service | JAR Location |
|---|---|
| Icglb | Icglb-Services-1.0/target/icglb-services-1.0.0.jar |
| BPM | ICoSys-BPM/ICoSys-BPM-Services-1.0/target/icosys-bpm-services-1.0.0.jar |
| DMS | ICoSys-DMS-Service-1.0/ICoSys-DMS-Service-1.0/target/icosys-dms-service-1.0.0.jar |
Frontend — Static Files¶
Output: dist/ directory containing optimized HTML, JS, and CSS.
Server Directory Structure¶
/opt/icosys/ # or C:\server\ICOSYS\
├── bin/
│ ├── icglb-services.jar
│ ├── icosys-bpm-services.jar
│ └── icosys-dms-services.jar
├── config/
│ ├── icglb/
│ │ └── application-prod.properties
│ ├── icbpm/
│ │ └── application-prod.properties
│ └── icdms/
│ └── application-prod.properties
├── data/
│ └── dms/ # DMS file storage
├── log/
│ ├── icglb/
│ ├── icbpm/
│ └── icdms/
└── web/
└── dist/ # React build output
Environment Variables¶
Required (All Environments)¶
| Variable | Description | Example |
|---|---|---|
DB_USERNAME |
MySQL user | icosys_app |
DB_PASSWORD |
MySQL password | (secret) |
JWT_SECRET |
HS512 signing key (64+ hex chars) | openssl rand -hex 32 |
ICGLB_SECURITY_API_KEY |
API key for protected endpoints | openssl rand -hex 16 |
Required (DMS Only)¶
| Variable | Description | Example |
|---|---|---|
DMS_URL_SECRET |
HMAC secret for signed download URLs | openssl rand -hex 16 |
ENCRYPTION_KEY |
AES key for encrypted fields (32 chars) | openssl rand -hex 16 |
Optional¶
| Variable | Description | Default |
|---|---|---|
SPRING_PROFILES_ACTIVE |
Active profile | dev |
JAVA_OPTS |
JVM options | -Xmx2g |
RECAPTCHA_SECRET_KEY |
Google reCAPTCHA v3 secret | — |
Secret Generation
Always generate fresh secrets for each environment:
# JWT secret (64 characters)
openssl rand -hex 32
# API key (32 characters)
openssl rand -hex 16
# Encryption key (32 characters)
openssl rand -hex 16
Never reuse dev defaults in test or production.
Database Setup¶
Create Dedicated User¶
-- Create application user (not root!)
CREATE USER 'icosys_app'@'%' IDENTIFIED BY 'strong_password_here';
-- Grant per-schema permissions
GRANT SELECT, INSERT, UPDATE, DELETE ON icglb2.* TO 'icosys_app'@'%';
GRANT SELECT, INSERT, UPDATE, DELETE ON icbpm.* TO 'icosys_app'@'%';
GRANT SELECT, INSERT, UPDATE, DELETE ON icdms.* TO 'icosys_app'@'%';
FLUSH PRIVILEGES;
No DDL Privileges
The application user should not have CREATE, ALTER, or DROP
privileges. Schema changes are applied manually via DDL scripts.
Run DDL Scripts¶
Execute DDL scripts in order:
Icglb-Services-1.0/docs/database/ddl_scripts.md(core tables first)ICoSys-BPM/.../docs/database/ddl_scripts.mdICoSys-DMS-Service-1.0/.../docs/database/ddl_scripts.md
Service Startup¶
Using systemd (Linux)¶
[Unit]
Description=ICOSYS Icglb Services
After=mysql.service
Requires=mysql.service
[Service]
Type=simple
User=icosys
WorkingDirectory=/opt/icosys/bin
ExecStart=/usr/bin/java \
-Xmx2g \
-jar icglb-services.jar \
--spring.profiles.active=prod \
--spring.config.additional-location=/opt/icosys/config/icglb/
Environment=DB_PASSWORD=your_password
Environment=JWT_SECRET=your_jwt_secret
Environment=ICGLB_SECURITY_API_KEY=your_api_key
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
Repeat for icbpm (port 8020) and icdms (port 8030).
Using Windows Service¶
Use WinSW or NSSM to register JARs as Windows services:
<service>
<id>icglb-services</id>
<name>ICOSYS Icglb Services</name>
<executable>java</executable>
<arguments>-Xmx2g -jar C:\server\ICOSYS\bin\icglb-services.jar --spring.profiles.active=prod</arguments>
<logpath>C:\server\ICOSYS\log\icglb</logpath>
<env name="DB_PASSWORD" value="your_password"/>
<env name="JWT_SECRET" value="your_jwt_secret"/>
</service>
Startup Order¶
Services are independent but the following order is recommended:
- MySQL — Database must be available
- Icglb-Services (8010) — Core auth, required for login
- BPM-Services (8020) — Optional, depends on business need
- DMS-Service (8030) — Optional, depends on business need
Reverse Proxy (Nginx)¶
All services sit behind a single Nginx reverse proxy with SSL termination.
server {
listen 443 ssl;
server_name app.icosys.com;
ssl_certificate /etc/ssl/certs/icosys.crt;
ssl_certificate_key /etc/ssl/private/icosys.key;
# React SPA
location / {
root /opt/icosys/web/dist;
try_files $uri $uri/ /index.html;
# Cache static assets
location ~* \.(js|css|png|jpg|svg|ico|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
# Backend services
location /icglb/services/ {
proxy_pass http://127.0.0.1:8010;
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;
}
location /icbpm/services/ {
proxy_pass http://127.0.0.1:8020;
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;
}
location /icdms/services/ {
proxy_pass http://127.0.0.1:8030;
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;
# DMS file upload — increase body size limit
client_max_body_size 100M;
}
}
# HTTP → HTTPS redirect
server {
listen 80;
server_name app.icosys.com;
return 301 https://$host$request_uri;
}
Health Checks¶
All services expose Spring Boot Actuator health endpoints:
| Service | Health URL |
|---|---|
| Icglb | http://localhost:8010/icglb/services/actuator/health |
| BPM | http://localhost:8020/icbpm/services/actuator/health |
| DMS | http://localhost:8030/icdms/services/actuator/health |
Monitoring Script¶
#!/bin/bash
SERVICES=(
"http://localhost:8010/icglb/services/actuator/health"
"http://localhost:8020/icbpm/services/actuator/health"
"http://localhost:8030/icdms/services/actuator/health"
)
for url in "${SERVICES[@]}"; do
status=$(curl -s -o /dev/null -w "%{http_code}" "$url" --max-time 5)
if [ "$status" != "200" ]; then
echo "ALERT: $url returned $status"
# Send notification (email, Slack, etc.)
fi
done
Log Management¶
Log Locations¶
| Service | Log File |
|---|---|
| Icglb | /opt/icosys/log/icglb/icglb-services.log |
| BPM | /opt/icosys/log/icbpm/icbpm-services.log |
| DMS | /opt/icosys/log/icdms/icdms-services.log |
Log Rotation¶
Spring Boot uses Logback with built-in rotation:
- Max file size: 10 MB
- History: 7 days
- Total cap: 100 MB per service
Production Log Levels¶
logging.level.root=WARN
logging.level.com.icom=INFO
logging.level.org.springframework=ERROR
logging.level.org.hibernate=ERROR
Security Checklist¶
Before Go-Live¶
- [ ] All secrets generated fresh (
JWT_SECRET,API_KEY,DMS_URL_SECRET,ENCRYPTION_KEY) - [ ]
DB_PASSWORDis not the default - [ ] MySQL user has no DDL privileges (SELECT, INSERT, UPDATE, DELETE only)
- [ ] CORS origins restricted to production domain(s)
- [ ] IP whitelist configured (if enabled)
- [ ] SSL/TLS enabled on reverse proxy
- [ ]
Secureflag on JWT cookie (HTTPS only) - [ ] Rate limiting enabled
- [ ] Health endpoints not publicly exposed (or behind auth)
- [ ] Log levels set to WARN/ERROR (no DEBUG)
- [ ] No dev default keys in any properties file
- [ ] DMS storage directory has correct file permissions
- [ ] Firewall: only ports 80/443 exposed, backend ports internal only
Cookie Security (Production)¶
CI/CD Pipeline¶
GitHub Actions (React Frontend)¶
The React project includes a GitHub Actions workflow that runs on every push:
See .github/workflows/ci.yml in ICoSys-Web-React-1.0/.
Backend CI¶
Each backend service can be analyzed with SonarQube:
mvn clean verify sonar:sonar \
-DskipTests \
-Dsonar.projectKey=icglb-services \
-Dsonar.host.url=$SONAR_HOST_URL \
-Dsonar.token=$SONAR_TOKEN
Rollback Procedure¶
Backend¶
- Stop the service:
sudo systemctl stop icglb - Replace JAR with previous version
- Start the service:
sudo systemctl start icglb - Verify health check
Frontend¶
- Replace
dist/with previous build - Nginx serves static files — no restart needed
Database¶
Database Rollback
DDL changes (new tables, columns) are not automatically reversible. Always create a database backup before applying DDL scripts:
Deployment Checklist¶
Per Release¶
- [ ] All tests pass locally
- [ ] CI pipeline green
- [ ] SonarQube Grade A (no blockers or criticals)
- [ ] Database backups taken
- [ ] DDL scripts reviewed and applied (if any)
- [ ] JARs built with correct profile
- [ ] React build completed (
npm run build) - [ ] Artifacts copied to server
- [ ] Services restarted
- [ ] Health checks pass
- [ ] Smoke test: login, list, create, update, delete
- [ ] Logs checked for errors in first 5 minutes
What's Next?¶
- Architecture Overview — System design and layers
- Security — Authentication and authorization deep dive
- Error Codes — Monitoring and troubleshooting with IC-* codes