What is DevOps?
DevOps is a culture and set of practices that bridges development and operations — the goal is to ship software faster, more reliably, and with fewer manual steps.
Core Pillars
- CI — Continuous Integration: merge code often, run tests automatically
- CD — Continuous Delivery/Deployment: automate the path to production
- IaC — Infrastructure as Code: manage servers via code, not clicks
- Monitoring — observe systems in production, alert on anomalies
Typical DevOps Toolchain
- Version control — Git / GitHub
- Containerisation — Docker, Kubernetes
- CI/CD — GitHub Actions, Jenkins, GitLab CI
- Cloud — AWS, GCP, Azure
- Monitoring — Prometheus, Grafana, ELK Stack
Linux Essentials
File System
ls -la # list files with permissions
cd /var/log # change directory
pwd # print working directory
mkdir -p app/logs # create nested dirs
rm -rf dist/ # remove recursively (careful!)
cp -r src/ backup/ # copy directory
mv old.txt new.txt # rename / move
cd /var/log # change directory
pwd # print working directory
mkdir -p app/logs # create nested dirs
rm -rf dist/ # remove recursively (careful!)
cp -r src/ backup/ # copy directory
mv old.txt new.txt # rename / move
Processes & Networking
ps aux | grep java # find Java processes
kill -9 1234 # force-kill PID 1234
netstat -tulnp # open ports
curl -I https://example.com # HTTP headers
tail -f /var/log/app.log # follow log output
kill -9 1234 # force-kill PID 1234
netstat -tulnp # open ports
curl -I https://example.com # HTTP headers
tail -f /var/log/app.log # follow log output
Permissions
chmod +x deploy.sh # make executable
chown ubuntu:ubuntu app/ # change owner
chown ubuntu:ubuntu app/ # change owner
Git & Version Control
git init # init repo
git clone <url> # clone remote
git status # show changes
git add . # stage all
git commit -m "feat: add auth" # commit
git push origin main # push to remote
git pull # fetch + merge
git checkout -b feature/login # create branch
git merge feature/login # merge branch
git stash # stash uncommitted changes
git log --oneline # compact history
git clone <url> # clone remote
git status # show changes
git add . # stage all
git commit -m "feat: add auth" # commit
git push origin main # push to remote
git pull # fetch + merge
git checkout -b feature/login # create branch
git merge feature/login # merge branch
git stash # stash uncommitted changes
git log --oneline # compact history
💡 Use Conventional Commits:
feat:, fix:, chore:, docs: — tools like semantic-release use them to auto-version your app.Docker Basics
Docker packages your app and all its dependencies into a portable container that runs identically everywhere.
docker pull nginx # pull image
docker run -d -p 80:80 nginx # run container
docker ps # list running containers
docker ps -a # all containers
docker stop <id> # stop container
docker rm <id> # remove container
docker images # list images
docker rmi <image> # remove image
docker logs -f <id> # follow logs
docker exec -it <id> /bin/bash # shell into container
docker run -d -p 80:80 nginx # run container
docker ps # list running containers
docker ps -a # all containers
docker stop <id> # stop container
docker rm <id> # remove container
docker images # list images
docker rmi <image> # remove image
docker logs -f <id> # follow logs
docker exec -it <id> /bin/bash # shell into container
Writing a Dockerfile
# Multi-stage build — small production image
FROM maven:3.9-eclipse-temurin-21 AS build
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
FROM maven:3.9-eclipse-temurin-21 AS build
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
docker build -t myapp:latest .
docker run -d -p 8080:8080 --name myapp myapp:latest
docker run -d -p 8080:8080 --name myapp myapp:latest
Docker Compose
Docker Compose defines multi-container apps in a single YAML file.
# docker-compose.yml
services:
app:
build: .
ports: ["8080:8080"]
environment:
- SPRING_PROFILES_ACTIVE=prod
depends_on: [db]
db:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: mydb
volumes: [db_data:/var/lib/mysql]
volumes:
db_data:
services:
app:
build: .
ports: ["8080:8080"]
environment:
- SPRING_PROFILES_ACTIVE=prod
depends_on: [db]
db:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: mydb
volumes: [db_data:/var/lib/mysql]
volumes:
db_data:
docker compose up -d # start in background
docker compose down # stop and remove
docker compose logs -f # follow all logs
docker compose down # stop and remove
docker compose logs -f # follow all logs
CI/CD Concepts
Continuous Integration (CI)
- Developers push code frequently — at least daily
- Each push triggers: build → test → static analysis
- Broken builds block merges — everyone is responsible for green CI
Continuous Delivery (CD)
- Every passing build is deployable to staging automatically
- Deployment to production requires a manual approval gate
Continuous Deployment
- No manual gate — every green build deploys to production automatically
- Requires high test coverage and feature flags for safety
💡 Start with CI + Continuous Delivery. Move to Continuous Deployment only once you trust your test suite.
GitHub Actions
# .github/workflows/deploy.yml
name: Build & Deploy
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with: { java-version: '21', distribution: 'temurin' }
- run: ./mvnw test
- run: ./mvnw package -DskipTests
- name: Deploy to server
run: ssh user@$SERVER "cd /app && ./deploy.sh"
env:
SERVER: ${{ secrets.SERVER_IP }}
name: Build & Deploy
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with: { java-version: '21', distribution: 'temurin' }
- run: ./mvnw test
- run: ./mvnw package -DskipTests
- name: Deploy to server
run: ssh user@$SERVER "cd /app && ./deploy.sh"
env:
SERVER: ${{ secrets.SERVER_IP }}
Nginx
Nginx acts as a reverse proxy in front of your Spring Boot app, handling SSL, compression, and static files.
# /etc/nginx/sites-available/myapp
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
sudo nginx -t # test config
sudo systemctl reload nginx
sudo systemctl reload nginx
AWS Basics
Core Services to Know
- EC2 — Virtual machines (your server)
- S3 — Object storage (images, backups, static sites)
- RDS — Managed relational database (MySQL, PostgreSQL)
- Route 53 — DNS management
- ACM — Free SSL certificates
- CloudFront — CDN for static assets
- ECR — Docker image registry
- ECS / EKS — Container orchestration
Typical Stack for a Java App
- EC2 t3.small → runs Spring Boot JAR behind Nginx
- RDS MySQL → managed database
- S3 → stores user uploads
- Route 53 → points domain to EC2
- ACM + Nginx → HTTPS
Monitoring & Logs
Spring Boot Actuator
# application.yml
management:
endpoints:
web:
exposure:
include: health,info,metrics
management:
endpoints:
web:
exposure:
include: health,info,metrics
Hit /actuator/health to see your app's health status — great for load balancer health checks.
Viewing Logs
tail -f /var/log/app/spring.log # follow live log
journalctl -u myapp -f # systemd service logs
docker logs -f myapp # Docker container logs
journalctl -u myapp -f # systemd service logs
docker logs -f myapp # Docker container logs
Tools
- Prometheus + Grafana — metrics collection and dashboards
- ELK Stack — Elasticsearch, Logstash, Kibana for log aggregation
- Uptime Kuma — lightweight self-hosted uptime monitoring