1 Commits

Author SHA1 Message Date
Tuan-Dat Tran
77ab7ba702 fix(ci): replace dependabot with renovate for Gitea compatibility
Dependabot only works on GitHub. This repo is hosted on Gitea which
requires Renovate for dependency updates.

- Remove .github/dependabot.yml (unused on Gitea)
- Add renovate.json with recommended config
- Configure npm package updates for / and /backend
- Configure GitHub Actions workflow updates
- Enable automerge for dev dependency minor/patch updates
2026-02-25 06:24:23 +01:00
14 changed files with 156 additions and 480 deletions

View File

@@ -1,32 +0,0 @@
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "06:00"
open-pull-requests-limit: 5
reviewers:
- "tuan-dat-tran"
labels:
- "dependencies"
commit-message:
prefix: "deps"
include: "scope"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "06:00"
open-pull-requests-limit: 3
reviewers:
- "tuan-dat-tran"
labels:
- "dependencies"
- "github-actions"
commit-message:
prefix: "ci"
include: "scope"

View File

@@ -1,9 +1,9 @@
FROM node:25-alpine as build FROM node:20-alpine as build
WORKDIR /app WORKDIR /app
COPY package*.json ./ COPY package*.json ./
RUN npm install --legacy-peer-deps --ignore-scripts RUN npm ci
COPY . . COPY . .
RUN npm run build RUN npm run build

View File

@@ -1,4 +1,4 @@
FROM node:25-slim FROM node:20-slim
WORKDIR /app WORKDIR /app

View File

@@ -24,192 +24,21 @@ export async function initDB() {
await db('cv_data').insert({ await db('cv_data').insert({
id: 1, id: 1,
data: JSON.stringify({ data: JSON.stringify({
"personal": { personal: {
"name": "Tuan-Dat Tran", name: "Tuan-Dat Tran",
"title": "Junior DevOps Engineer", title: "Junior DevOps Engineer",
"intro": "DevOps Engineer and Homelab Enthusiast based in Essen, Germany. Primary owner of production SaaS platforms on Azure AKS, with a strong background in Linux, automation, and infrastructure engineering.", intro: "Passionierter DevOps Engineer mit Fokus auf Cloud-Infrastruktur.",
"email": "tuan-dat.tran@dextradata.com", email: "tuan-dat.tran@example.com",
"github": "https://github.com/TuDatTr", github: "https://github.com/tuan-dat-tran",
"linkedin": "https://www.linkedin.com/in/tudattr/", linkedin: "https://linkedin.com/in/tuan-dat-tran",
"location": "Essen, Germany" location: "Deutschland"
}, },
"experience": [ experience: [],
{ skills: {},
"id": 1, education: [],
"role": "Junior DevOps Engineer", projects: []
"company": "DextraData GRC Technologies GmbH", })
"period": "2025 present", });
"type": "Full-time",
"description": "Primary owner of the GRASP GRC platform and Logipad aviation SaaS platform, running on Azure AKS and Rancher-managed Kubernetes clusters.",
"highlights": [
"Drives GitOps adoption with ArgoCD across production Kubernetes clusters",
"Manages CI/CD pipelines with Bitbucket Pipelines",
"Leads incident response and on-call for production outages",
"Reduced GRASP frontend pipeline from 30 to 16 minutes",
"Eliminated ~$2k/month Azure logging costs via workspace restructuring",
"Built internal AI tooling with LiteLLM and Ollama",
"Implemented Azure Container Registry mirroring to resolve Docker Hub rate limits"
]
},
{
"id": 2,
"role": "Research Assistant",
"company": "NCS Group, Universität Duisburg-Essen",
"period": "2021 2024",
"type": "Part-time",
"description": "University research group role covering infrastructure management, containerized systems, and networking experiments.",
"highlights": [
"Managed lab infrastructure: Ansible-automated servers, AWS accounts, and inventory systems",
"Built and operated Dockerized and Kubernetes-based experiment environments",
"Co-authored a peer-reviewed publication at IEEE LCN 2023"
]
},
{
"id": 3,
"role": "Student Mentor",
"company": "Universität Duisburg-Essen",
"period": "2021 2022",
"type": "Volunteer",
"description": "Introduced groups of ~20 freshmen to their new academic environment each semester, providing organizational and technical guidance throughout their first year.",
"highlights": [
"Guided new students through academic orientation each semester",
"Provided technical and organizational support throughout the first year"
]
},
{
"id": 4,
"role": "Software Engineer",
"company": "gefeba Engineering GmbH",
"period": "2018 2020",
"type": "Part-time",
"description": "Worked on the company's main product — a frame-based data exchange system for monitoring industrial machinery — and developed a real-time log visualization application.",
"highlights": [
"Developed features for a frame-based industrial data exchange system",
"Built a real-time log visualization application for industrial machinery",
"Stack: C#, Angular, Bootstrap, Entity Framework"
]
},
{
"id": 5,
"role": "Student Council Member",
"company": "Universität Duisburg-Essen",
"period": "2016 2019",
"type": "Volunteer",
"description": "Participated in faculty committees and organized social events. Managed the faculty's IT infrastructure and provided support to fellow students.",
"highlights": [
"Managed faculty IT infrastructure (Linux, Networking, LaTeX)",
"Organized social events and participated in faculty committees",
"Provided subject-specific and organizational support to fellow students"
]
},
{
"id": 6,
"role": "Software Engineer",
"company": "gefeba Engineering GmbH",
"period": "2013 2015",
"type": "Part-time",
"description": "Joined after a school internship. Worked on internal ERP projects, designed a mail traffic management tool, and contributed to master data management tooling.",
"highlights": [
"Developed internal ERP project features",
"Designed a tool for managing project-related mail traffic",
"Contributed to internal master data management tooling",
"Stack: C#, HTML, CSS, JavaScript"
]
}
],
"skills": {
"Container & Orchestration": [
"Kubernetes",
"Docker",
"Helm",
"ArgoCD"
],
"CI/CD": [
"Bitbucket Pipelines",
"Git"
],
"Cloud": [
"Azure",
"Azure AKS",
"Azure App Gateway",
"Azure Entra ID"
],
"Infrastructure as Code": [
"Ansible"
],
"Observability": [
"ELK Stack",
"Checkmk",
"UptimeRobot"
],
"Databases": [
"PostgreSQL",
"MongoDB",
"CouchDB"
],
"Programming": [
"Rust",
"Python",
"Bash"
]
},
"education": [
{
"id": 1,
"degree": "B.Sc. Systems Engineering",
"institution": "Universität Duisburg-Essen",
"period": "2015 present",
"status": "Bachelor thesis ongoing"
}
],
"projects": [
{
"id": 1,
"name": "Ethereum Smart Contract Fuzzer",
"description": "A fuzzer for Ethereum smart contracts built as part of the bachelor thesis project.",
"url": "#",
"tech": [
"Rust"
]
},
{
"id": 2,
"name": "Homelab",
"description": "Ansible-managed homelab running on Debian Linux with Raspberry Pi, Mikrotik networking, Pihole, and self-hosted services.",
"url": "https://git.tudattr.dev/tudattr/ansible",
"tech": [
"Ansible",
"Kubernetes",
"Debian Linux"
]
},
{
"id": 3,
"name": ".dotfiles",
"description": "Configuration files for daily-use tools providing a reproducible baseline for any personal ArchLinux system, including NeoVim (LazyVim), Zellij, and k9s.",
"url": "https://github.com/TuDatTr/dotfiles",
"tech": [
"Zsh",
"NeoVim",
"Zellij",
"ArchLinux"
]
},
{
"id": 4,
"name": "tudattr.dev",
"description": "Full-stack WebAssembly personal website built with the Rust-based Dioxus framework and Tailwind CSS.",
"url": "https://www.tudattr.dev",
"tech": [
"Rust",
"Dioxus",
"Tailwind CSS",
"WebAssembly"
]
}
]
})
});
console.log('Initialized database with default CV data'); console.log('Initialized database with default CV data');
} }

View File

@@ -9,9 +9,9 @@
"test:watch": "vitest" "test:watch": "vitest"
}, },
"dependencies": { "dependencies": {
"better-sqlite3": "^12.0.0", "better-sqlite3": "^11.7.0",
"cors": "^2.8.5", "cors": "^2.8.5",
"express": "^5.0.0", "express": "^4.21.2",
"knex": "^3.1.0", "knex": "^3.1.0",
"swagger-jsdoc": "^6.2.8", "swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.1" "swagger-ui-express": "^5.0.1"

View File

@@ -4,7 +4,6 @@
"version": "1.1.0", "version": "1.1.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"sync-profile": "node scripts/sync-profile.mjs",
"dev": "vite", "dev": "vite",
"build": "vite build", "build": "vite build",
"lint": "eslint .", "lint": "eslint .",
@@ -33,7 +32,7 @@
], ],
"dependencies": { "dependencies": {
"framer-motion": "^12.34.1", "framer-motion": "^12.34.1",
"lucide-react": "^0.577.0", "lucide-react": "^0.574.0",
"react": "^19.2.0", "react": "^19.2.0",
"react-dom": "^19.2.0", "react-dom": "^19.2.0",
"react-router-dom": "^7.13.0" "react-router-dom": "^7.13.0"
@@ -41,7 +40,7 @@
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^20.4.2", "@commitlint/cli": "^20.4.2",
"@commitlint/config-conventional": "^20.4.2", "@commitlint/config-conventional": "^20.4.2",
"@eslint/js": "^10.0.0", "@eslint/js": "^9.39.1",
"@lhci/cli": "^0.15.1", "@lhci/cli": "^0.15.1",
"@playwright/test": "^1.58.2", "@playwright/test": "^1.58.2",
"@semantic-release/changelog": "^6.0.3", "@semantic-release/changelog": "^6.0.3",
@@ -52,24 +51,24 @@
"@testing-library/react": "^16.3.2", "@testing-library/react": "^16.3.2",
"@types/react": "^19.2.7", "@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.0", "@vitejs/plugin-react": "^5.1.1",
"@vitest/coverage-v8": "^4.0.18", "@vitest/coverage-v8": "^4.0.18",
"autoprefixer": "^10.4.24", "autoprefixer": "^10.4.24",
"better-sqlite3": "^12.6.2", "better-sqlite3": "^12.6.2",
"bundlesize": "^0.18.2", "bundlesize": "^0.18.2",
"cors": "^2.8.6", "cors": "^2.8.6",
"eslint": "^10.0.0", "eslint": "^9.39.1",
"eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.5.0", "eslint-plugin-react-refresh": "^0.4.24",
"express": "^5.2.1", "express": "^5.2.1",
"globals": "^17.0.0", "globals": "^16.5.0",
"husky": "^9.1.7", "husky": "^9.1.7",
"jsdom": "^28.1.0", "jsdom": "^28.1.0",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"semantic-release": "^25.0.3", "semantic-release": "^25.0.3",
"supertest": "^7.2.2", "supertest": "^7.2.2",
"tailwindcss": "^4.1.18", "tailwindcss": "^4.1.18",
"vite": "^8.0.0", "vite": "^7.3.1",
"vitest": "^4.0.18" "vitest": "^4.0.18"
} }
} }

View File

@@ -1,3 +1,37 @@
{ {
"$schema": "https://docs.renovatebot.com/renovate-schema.json" "$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
],
"timezone": "Europe/Berlin",
"schedule": ["on monday before 10:00am"],
"prConcurrentLimit": 5,
"prHourlyLimit": 2,
"labels": ["dependencies"],
"commitMessagePrefix": "deps",
"commitMessageAction": "update",
"packageRules": [
{
"matchManagers": ["npm"],
"rangeStrategy": "bump",
"semanticCommitType": "deps",
"semanticCommitScope": "{{packageFileDir}}"
},
{
"matchManagers": ["github-actions"],
"semanticCommitType": "ci",
"semanticCommitScope": "actions",
"groupName": "github-actions"
},
{
"matchDepTypes": ["devDependencies"],
"matchUpdateTypes": ["minor", "patch"],
"automerge": true
}
],
"lockFileMaintenance": {
"enabled": true,
"schedule": ["on the first day of the month"]
},
"postUpdateOptions": ["npmDedupe"]
} }

View File

@@ -1,60 +0,0 @@
#!/usr/bin/env node
/**
* Reads cv-data.json from the persons profile directory and writes it into:
* - src/lib/cv.json (frontend default / compile-time fallback)
* - backend/db/init.js (backend DB seed on first boot)
*
* Usage:
* node scripts/sync-profile.mjs [path/to/cv-data.json]
*
* Default source path: ~/workspace/persons/tuan-dat.tran/cv-data.json
*/
import { readFileSync, writeFileSync } from 'fs';
import { resolve } from 'path';
import { homedir } from 'os';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
const __dirname = dirname(fileURLToPath(import.meta.url));
const root = resolve(__dirname, '..');
const sourcePath = process.argv[2]
?? resolve(homedir(), 'workspace/persons/tuan-dat.tran/cv-data.json');
let data;
try {
data = JSON.parse(readFileSync(sourcePath, 'utf8'));
} catch (e) {
console.error(`Failed to read ${sourcePath}: ${e.message}`);
process.exit(1);
}
// 1. Write src/lib/cv.json
const cvJsonPath = resolve(root, 'src/lib/cv.json');
writeFileSync(cvJsonPath, JSON.stringify(data, null, 2) + '\n');
console.log(`wrote ${cvJsonPath}`);
// 2. Rewrite the seed block in backend/db/init.js
const initPath = resolve(root, 'backend/db/init.js');
const initSrc = readFileSync(initPath, 'utf8');
const seedJson = JSON.stringify(data, null, 8)
.split('\n')
.map((line, i) => i === 0 ? line : ' ' + line)
.join('\n');
const updated = initSrc.replace(
/data: JSON\.stringify\([\s\S]*?\)\s*\}\);/,
`data: JSON.stringify(${seedJson})\n });`
);
if (updated === initSrc) {
console.error('Could not find seed block in backend/db/init.js — pattern mismatch');
process.exit(1);
}
writeFileSync(initPath, updated);
console.log(`wrote ${initPath}`);
console.log('done — rebuild Docker images and restart with a fresh volume to apply');

View File

@@ -7,20 +7,25 @@ export default function Education() {
const { education } = data; const { education } = data;
return ( return (
<section id="education" className="py-12 px-6"> <section id="education" className="py-20 px-6">
<div className="max-w-4xl mx-auto"> <div className="max-w-4xl mx-auto">
<motion.h2 <motion.h2
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ opacity: 1 }} whileInView={{ opacity: 1 }}
viewport={{ once: true }}
className="text-3xl font-bold text-slate-900 mb-12 text-center" className="text-3xl font-bold text-slate-900 mb-12 text-center"
> >
Education Ausbildung
</motion.h2> </motion.h2>
<div className="grid md:grid-cols-2 gap-6"> <div className="grid md:grid-cols-2 gap-6">
{education.map((edu, index) => ( {education.map((edu, index) => (
<div <motion.div
key={edu.id} key={edu.id}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: index * 0.2 }}
className="bg-white rounded-xl p-6 shadow-sm border border-slate-100" className="bg-white rounded-xl p-6 shadow-sm border border-slate-100"
> >
<div className="flex items-start gap-4"> <div className="flex items-start gap-4">
@@ -39,7 +44,7 @@ export default function Education() {
</span> </span>
</div> </div>
</div> </div>
</div> </motion.div>
))} ))}
</div> </div>
</div> </div>

View File

@@ -7,20 +7,25 @@ export default function Experience() {
const { experience } = data; const { experience } = data;
return ( return (
<section id="experience" className="py-12 px-6"> <section id="experience" className="py-20 px-6">
<div className="max-w-4xl mx-auto"> <div className="max-w-4xl mx-auto">
<motion.h2 <motion.h2
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ opacity: 1 }} whileInView={{ opacity: 1 }}
viewport={{ once: true }}
className="text-3xl font-bold text-slate-900 mb-12 text-center" className="text-3xl font-bold text-slate-900 mb-12 text-center"
> >
Experience Berufserfahrung
</motion.h2> </motion.h2>
<div className="relative"> <div className="relative">
{experience.map((exp, index) => ( {experience.map((exp, index) => (
<div <motion.div
key={exp.id} key={exp.id}
initial={{ opacity: 0, x: -20 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }}
transition={{ delay: index * 0.2 }}
className="relative pl-8 pb-12 last:pb-0" className="relative pl-8 pb-12 last:pb-0"
> >
<div className="absolute left-0 top-2 w-4 h-4 rounded-full bg-primary-500 border-4 border-primary-100" /> <div className="absolute left-0 top-2 w-4 h-4 rounded-full bg-primary-500 border-4 border-primary-100" />
@@ -58,7 +63,7 @@ export default function Experience() {
))} ))}
</ul> </ul>
</div> </div>
</div> </motion.div>
))} ))}
</div> </div>
</div> </div>

View File

@@ -7,7 +7,7 @@ export default function Hero() {
const { personal } = data; const { personal } = data;
return ( return (
<section className="flex items-center justify-center px-6 py-16"> <section className="min-h-screen flex items-center justify-center px-6 py-20">
<motion.div <motion.div
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}

View File

@@ -7,14 +7,15 @@ export default function Projects() {
const { projects } = data; const { projects } = data;
return ( return (
<section id="projects" className="py-12 px-6 bg-white"> <section id="projects" className="py-20 px-6 bg-white">
<div className="max-w-4xl mx-auto"> <div className="max-w-4xl mx-auto">
<motion.h2 <motion.h2
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ opacity: 1 }} whileInView={{ opacity: 1 }}
viewport={{ once: true }}
className="text-3xl font-bold text-slate-900 mb-12 text-center" className="text-3xl font-bold text-slate-900 mb-12 text-center"
> >
Projects Projekte
</motion.h2> </motion.h2>
<div className="grid md:grid-cols-2 gap-6"> <div className="grid md:grid-cols-2 gap-6">
@@ -24,6 +25,10 @@ export default function Projects() {
href={project.url} href={project.url}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: index * 0.2 }}
whileHover={{ y: -4 }} whileHover={{ y: -4 }}
className="group bg-slate-50 rounded-xl p-6 border border-slate-100 hover:border-primary-200 hover:shadow-md transition-all" className="group bg-slate-50 rounded-xl p-6 border border-slate-100 hover:border-primary-200 hover:shadow-md transition-all"
> >

View File

@@ -5,22 +5,17 @@ const skillIcons = {
"Docker": "🐳", "Docker": "🐳",
"Kubernetes": "☸️", "Kubernetes": "☸️",
"Helm": "⛵", "Helm": "⛵",
"ArgoCD": "🔄",
"Jenkins": "🔧", "Jenkins": "🔧",
"Bitbucket Pipelines": "📘", "Bitbucket": "📘",
"ArgoCD": "🔄",
"Git": "📝", "Git": "📝",
"Azure": "☁️", "Azure": "☁️",
"Azure AKS": "", "Azure Resource Manager": "🏗",
"Ansible": "⚙️", "Release Engineering": "🚀",
"ELK Stack": "📊", "Linux Administration": "🐧",
"PostgreSQL": "🐘",
"MongoDB": "🍃",
"Rust": "🦀",
"Python": "🐍", "Python": "🐍",
"Bash": "💻", "Bash": "💻",
"P4": "🔬", "Rust": "🦀"
"Intel Tofino": "🔬",
"Linux Administration": "🐧",
}; };
export default function Skills() { export default function Skills() {
@@ -28,11 +23,12 @@ export default function Skills() {
const { skills } = data; const { skills } = data;
return ( return (
<section id="skills" className="py-12 px-6 bg-white"> <section id="skills" className="py-20 px-6 bg-white">
<div className="max-w-4xl mx-auto"> <div className="max-w-4xl mx-auto">
<motion.h2 <motion.h2
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ opacity: 1 }} whileInView={{ opacity: 1 }}
viewport={{ once: true }}
className="text-3xl font-bold text-slate-900 mb-12 text-center" className="text-3xl font-bold text-slate-900 mb-12 text-center"
> >
Skills Skills
@@ -40,8 +36,12 @@ export default function Skills() {
<div className="grid md:grid-cols-2 gap-8"> <div className="grid md:grid-cols-2 gap-8">
{Object.entries(skills).map(([category, items], catIndex) => ( {Object.entries(skills).map(([category, items], catIndex) => (
<div <motion.div
key={category} key={category}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: catIndex * 0.1 }}
className="bg-slate-50 rounded-xl p-6" className="bg-slate-50 rounded-xl p-6"
> >
<h3 className="text-lg font-semibold text-primary-600 mb-4"> <h3 className="text-lg font-semibold text-primary-600 mb-4">
@@ -51,6 +51,10 @@ export default function Skills() {
{items.map((skill, i) => ( {items.map((skill, i) => (
<motion.span <motion.span
key={skill} key={skill}
initial={{ opacity: 0, scale: 0.8 }}
whileInView={{ opacity: 1, scale: 1 }}
viewport={{ once: true }}
transition={{ delay: i * 0.05 }}
whileHover={{ scale: 1.05 }} whileHover={{ scale: 1.05 }}
className="px-3 py-1.5 bg-white rounded-lg text-sm text-slate-700 shadow-sm border border-slate-100 flex items-center gap-1.5 cursor-default" className="px-3 py-1.5 bg-white rounded-lg text-sm text-slate-700 shadow-sm border border-slate-100 flex items-center gap-1.5 cursor-default"
> >
@@ -59,7 +63,7 @@ export default function Skills() {
</motion.span> </motion.span>
))} ))}
</div> </div>
</div> </motion.div>
))} ))}
</div> </div>
</div> </div>

View File

@@ -2,185 +2,72 @@
"personal": { "personal": {
"name": "Tuan-Dat Tran", "name": "Tuan-Dat Tran",
"title": "Junior DevOps Engineer", "title": "Junior DevOps Engineer",
"intro": "DevOps Engineer and Homelab Enthusiast based in Essen, Germany. Primary owner of production SaaS platforms on Azure AKS, with a strong background in Linux, automation, and infrastructure engineering.", "intro": "Passionierter DevOps Engineer mit Fokus auf Cloud-Infrastruktur, Container-Orchestrierung und automatisierte Deployment-Pipelines.",
"email": "tuan-dat.tran@dextradata.com", "email": "tuan-dat.tran@example.com",
"github": "https://github.com/TuDatTr", "github": "https://github.com/tuan-dat-tran",
"linkedin": "https://www.linkedin.com/in/tudattr/", "linkedin": "https://linkedin.com/in/tuan-dat-tran",
"location": "Essen, Germany" "location": "Deutschland"
}, },
"experience": [ "experience": [
{ {
"id": 1, "id": 1,
"role": "Junior DevOps Engineer", "role": "Junior DevOps Engineer",
"company": "DextraData GRC Technologies GmbH", "company": "Unternehmen",
"period": "2025 present", "period": "Seit 2025",
"type": "Full-time", "type": "Vollzeit",
"description": "Primary owner of the GRASP GRC platform and Logipad aviation SaaS platform, running on Azure AKS and Rancher-managed Kubernetes clusters.", "description": "Verantwortlich für CI/CD Pipelines, Container-Orchestrierung und Cloud-Infrastruktur auf Azure.",
"highlights": [ "highlights": [
"Drives GitOps adoption with ArgoCD across production Kubernetes clusters", "Automatisierte Deployments mit Jenkins und Bitbucket Pipelines",
"Manages CI/CD pipelines with Bitbucket Pipelines", "Kubernetes Cluster Management mit Helm und ArgoCD",
"Leads incident response and on-call for production outages", "Infrastructure as Code mit Azure Resource Manager"
"Reduced GRASP frontend pipeline from 30 to 16 minutes",
"Eliminated ~$2k/month Azure logging costs via workspace restructuring",
"Built internal AI tooling with LiteLLM and Ollama",
"Implemented Azure Container Registry mirroring to resolve Docker Hub rate limits"
] ]
}, },
{ {
"id": 2, "id": 2,
"role": "Research Assistant", "role": "Software Entwickler",
"company": "NCS Group, Universität Duisburg-Essen", "company": "Verschiedene Unternehmen",
"period": "2021 2024", "period": "2021 - 2025",
"type": "Part-time", "type": "Teilzeit / Werkstudent / SHK",
"description": "University research group role covering infrastructure management, containerized systems, and networking experiments.", "description": "Entwicklung und Maintenance von Softwarelösungen in verschiedenen Rollen.",
"highlights": [ "highlights": [
"Managed lab infrastructure: Ansible-automated servers, AWS accounts, and inventory systems", "Backend-Entwicklung mit Python",
"Built and operated Dockerized and Kubernetes-based experiment environments", "Linux System Administration",
"Co-authored a peer-reviewed publication at IEEE LCN 2023" "Git Version Control und Release Engineering"
]
},
{
"id": 3,
"role": "Student Mentor",
"company": "Universität Duisburg-Essen",
"period": "2021 2022",
"type": "Volunteer",
"description": "Introduced groups of ~20 freshmen to their new academic environment each semester, providing organizational and technical guidance throughout their first year.",
"highlights": [
"Guided new students through academic orientation each semester",
"Provided technical and organizational support throughout the first year"
]
},
{
"id": 4,
"role": "Software Engineer",
"company": "gefeba Engineering GmbH",
"period": "2018 2020",
"type": "Part-time",
"description": "Worked on the company's main product — a frame-based data exchange system for monitoring industrial machinery — and developed a real-time log visualization application.",
"highlights": [
"Developed features for a frame-based industrial data exchange system",
"Built a real-time log visualization application for industrial machinery",
"Stack: C#, Angular, Bootstrap, Entity Framework"
]
},
{
"id": 5,
"role": "Student Council Member",
"company": "Universität Duisburg-Essen",
"period": "2016 2019",
"type": "Volunteer",
"description": "Participated in faculty committees and organized social events. Managed the faculty's IT infrastructure and provided support to fellow students.",
"highlights": [
"Managed faculty IT infrastructure (Linux, Networking, LaTeX)",
"Organized social events and participated in faculty committees",
"Provided subject-specific and organizational support to fellow students"
]
},
{
"id": 6,
"role": "Software Engineer",
"company": "gefeba Engineering GmbH",
"period": "2013 2015",
"type": "Part-time",
"description": "Joined after a school internship. Worked on internal ERP projects, designed a mail traffic management tool, and contributed to master data management tooling.",
"highlights": [
"Developed internal ERP project features",
"Designed a tool for managing project-related mail traffic",
"Contributed to internal master data management tooling",
"Stack: C#, HTML, CSS, JavaScript"
] ]
} }
], ],
"skills": { "skills": {
"Container & Orchestration": [ "Container & Orchestration": ["Docker", "Kubernetes", "Helm"],
"Kubernetes", "CI/CD & GitOps": ["Jenkins", "Bitbucket", "ArgoCD", "Git"],
"Docker", "Cloud": ["Azure"],
"Helm", "Infrastructure as Code": ["Azure Resource Manager"],
"ArgoCD" "Release Engineering": ["Release Engineering"],
], "Operating Systems": ["Linux Administration"],
"CI/CD": [ "Programming": ["Python", "Bash", "Rust"]
"Bitbucket Pipelines",
"Git"
],
"Cloud": [
"Azure",
"Azure AKS",
"Azure App Gateway",
"Azure Entra ID"
],
"Infrastructure as Code": [
"Ansible"
],
"Observability": [
"ELK Stack",
"Checkmk",
"UptimeRobot"
],
"Databases": [
"PostgreSQL",
"MongoDB",
"CouchDB"
],
"Programming": [
"Rust",
"Python",
"Bash"
]
}, },
"education": [ "education": [
{ {
"id": 1, "id": 1,
"degree": "B.Sc. Systems Engineering", "degree": "Bachelor Informatik",
"institution": "Universität Duisburg-Essen", "institution": "Universität",
"period": "2015 present", "period": "Laufend",
"status": "Bachelor thesis ongoing" "status": "Bachelorarbeit offen"
} }
], ],
"projects": [ "projects": [
{ {
"id": 1, "id": 1,
"name": "Ethereum Smart Contract Fuzzer", "name": "Projekt 1",
"description": "A fuzzer for Ethereum smart contracts built as part of the bachelor thesis project.", "description": "Beschreibung folgt",
"url": "#", "url": "#",
"tech": [ "tech": ["Docker", "Kubernetes", "ArgoCD"]
"Rust"
]
}, },
{ {
"id": 2, "id": 2,
"name": "Homelab", "name": "Projekt 2",
"description": "Ansible-managed homelab running on Debian Linux with Raspberry Pi, Mikrotik networking, Pihole, and self-hosted services.", "description": "Beschreibung folgt",
"url": "https://git.tudattr.dev/tudattr/ansible", "url": "#",
"tech": [ "tech": ["Azure", "ARM", "Jenkins"]
"Ansible",
"Kubernetes",
"Debian Linux"
]
},
{
"id": 3,
"name": ".dotfiles",
"description": "Configuration files for daily-use tools providing a reproducible baseline for any personal ArchLinux system, including NeoVim (LazyVim), Zellij, and k9s.",
"url": "https://github.com/TuDatTr/dotfiles",
"tech": [
"Zsh",
"NeoVim",
"Zellij",
"ArchLinux"
]
},
{
"id": 4,
"name": "tudattr.dev",
"description": "Full-stack WebAssembly personal website built with the Rust-based Dioxus framework and Tailwind CSS.",
"url": "https://www.tudattr.dev",
"tech": [
"Rust",
"Dioxus",
"Tailwind CSS",
"WebAssembly"
]
} }
] ]
} }