中文 | English
KUNO /ˈkuːnoʊ/
The name draws inspiration from the fusion of Eastern philosophy and modern technological concepts:
- "KUN" is the pinyin of "坤" (kūn) from the I Ching (Book of Changes), meaning earth, carrying all things, symbolizing the system's comprehensive support and inclusiveness for multilingual and multi-format content.
- "O" represents Origin and Open, symbolizing the beginning of content and the system's openness, extensibility, and modern architectural design philosophy.
Our logo is inspired by the Kun hexagram ☷ — six lines representing earth, harmony, carrying capacity, and inclusiveness, which also embodies KUNO system's core values.
KUNO is designed as a lightweight, blazing-fast, i18n-first CMS, empowering creators to build rich, structured content ecosystems with freedom and harmony.
A full-stack blog application with Go backend and Next.js frontend, containerized with Docker for easy deployment.
- 📝 Blog Management: Create, edit, and delete articles
- 🌍 Multi-language Support: 70+ languages interface
- 🏷️ Category System: Organize posts by categories
- 🌙 Dark Mode: Toggle between light and dark themes
- 📱 Responsive Design: Mobile-first responsive layout
- ⚡ Fast Performance: Optimized with modern frameworks
- 🔒 Admin Panel: Complete content management system
- ⚙️ Site Settings: Customizable site title and subtitle
- 🖋️ Markdown Editor: Rich text editing with live preview
- 🐳 Docker Ready: One-click deployment with Docker
The system supports interface switching for 70+ languages:
| Region | Languages |
|---|---|
| Core | 🇨🇳 Chinese • 🇬🇧 English |
| Asian | 🇯🇵 Japanese • 🇰🇷 Korean • 🇹🇭 Thai • 🇻🇳 Vietnamese • 🇮🇩 Indonesian • 🇲🇾 Malay • 🇵🇭 Filipino • 🇲🇲 Myanmar • 🇰🇭 Khmer • 🇱🇦 Lao |
| European | 🇪🇸 Spanish • 🇫🇷 French • 🇩🇪 German • 🇷🇺 Russian • 🇵🇹 Portuguese • 🇮🇹 Italian • 🇳🇱 Dutch • 🇸🇪 Swedish • 🇩🇰 Danish • 🇳🇴 Norwegian • 🇫🇮 Finnish • 🇵🇱 Polish • 🇨🇿 Czech • 🇸🇰 Slovak • 🇭🇺 Hungarian • 🇷🇴 Romanian • 🇧🇬 Bulgarian • 🇭🇷 Croatian • 🇷🇸 Serbian • 🇸🇮 Slovenian • 🇪🇪 Estonian • 🇱🇻 Latvian • 🇱🇹 Lithuanian • 🇺🇦 Ukrainian • 🇧🇾 Belarusian • 🇹🇷 Turkish • 🇬🇷 Greek • 🇦🇱 Albanian • 🇦🇲 Armenian • 🇦🇿 Azerbaijani • 🇬🇪 Georgian |
| Middle Eastern & African | 🇸🇦 Arabic • 🇮🇱 Hebrew • 🇮🇷 Persian • 🇵🇰 Urdu • 🇪🇹 Amharic • 🇰🇪 Swahili • 🇿🇦 Zulu • 🇿🇦 Afrikaans |
| South Asian | 🇮🇳 Hindi • 🇧🇩 Bengali • 🇮🇳 Tamil • 🇮🇳 Telugu • 🇮🇳 Malayalam • 🇮🇳 Kannada • 🇮🇳 Gujarati • 🇮🇳 Punjabi • 🇮🇳 Marathi • 🇳🇵 Nepali • 🇱🇰 Sinhala |
| Pacific & Others | 🇳🇿 Māori • 🇼🇸 Samoan • 🇹🇴 Tongan • 🇫🇯 Fijian • 🇮🇪 Irish • 🇮🇸 Icelandic • 🇲🇹 Maltese • 🇪🇸 Basque • 🇪🇸 Catalan |
Create a dedicated directory and deploy:
# 1. Create dedicated directory (recommended: /opt) sudo mkdir -p /opt/kuno cd /opt/kuno # 2. Download and execute deployment script curl -sSL "https://raw.githubusercontent.com/xuemian168/kuno/main/deploy-from-hub.sh?$(date +%s)" -o deploy.sh && chmod +x deploy.sh && ./deploy.sh
The deployment script will guide you through a simplified configuration process:
- Choose Protocol: HTTP or HTTPS (HTTPS recommended)
- Enter Domain: e.g.,
qut.edu.kg - Auto-construct API URL: The system will automatically generate the complete API URL, e.g.,
https://qut.edu.kg/api
Important Notes:
- Do not use
curl | bashas it will cause syntax errors- Deploy in
/opt/kunoto avoid cluttering the home directory- The new deployment script simplifies the configuration process - users only need to select protocol and enter domain
# 1. Create dedicated directory sudo mkdir -p /opt/kuno cd /opt/kuno # 2. Create data directory mkdir -p ./blog-data # 3. Run container docker run -d \ --name kuno \ --restart unless-stopped \ -p 80:80 \ -v /opt/kuno/blog-data:/app/data \ -e NEXT_PUBLIC_API_URL="http://localhost/api" \ -e DB_PATH="/app/data/blog.db" \ -e GIN_MODE="release" \ -e NODE_ENV="production" \ -e JWT_SECRET="your-secure-secret-key" \ ictrun/kuno:latest
NEXT_PUBLIC_API_URL- MUST be modified according to your network environment- Local access:
http://localhost/apiorhttp://127.0.0.1/api - LAN access:
http://192.168.1.100/api(use your actual IP) - Public domain:
https://yourdomain.com/api - Non-80 port:
http://localhost:8080/api
- Local access:
JWT_SECRET- Strongly recommended for production- Secret key for signing JWT tokens
- If not set, a random key is auto-generated (changes on restart)
- Use a complex string of at least 32 characters
Directory Structure:
/opt/kuno/- Application main directory/opt/kuno/blog-data/- Data storage (database and uploads)/opt/kuno/deploy.sh- Deployment script (one-click method)
-
Clone the repository:
git clone <repository-url> cd blog
-
Start the application:
./start.sh
-
Access the application:
- Frontend: http://localhost:3000
- API: http://localhost:8080/api
- Admin Panel: http://localhost:3000/admin
# Download the compose file curl -O https://raw.githubusercontent.com/xuemian168/kuno/main/docker-compose.hub.yml # Configure environment cp .env.hub.example .env # Edit .env with your settings # Deploy docker-compose -f docker-compose.hub.yml up -d
# Create dedicated directory cd /opt/kuno # Run container with bind mount (modify NEXT_PUBLIC_API_URL!) docker run -d \ --name kuno \ --restart unless-stopped \ -p 80:80 \ -v /opt/kuno/blog-data:/app/data \ -e NEXT_PUBLIC_API_URL=https://your-api-domain.com/api \ -e DB_PATH=/app/data/blog.db \ ictrun/kuno:latest
ictrun/kuno:latest- Latest stable releaseictrun/kuno:v1.0.0- Specific versionictrun/kuno:develop- Development branch
| Variable | Default | Description |
|---|---|---|
NEXT_PUBLIC_API_URL |
https://your-domain.com/api |
Your API endpoint URL |
DB_PATH |
/app/data/blog.db |
SQLite database path |
GIN_MODE |
release |
Go Gin mode (release/debug) |
NODE_ENV |
production |
Node.js environment |
RECOVERY_MODE |
false |
Password recovery mode |
JWT_SECRET |
(auto-generated) | JWT signing secret (recommended for production) |
- Access the blog: http://localhost (or your domain)
- Admin login: http://localhost/admin
- Username:
admin - Password:
xuemian168
- Username:
⚠️ Important: Change the default password immediately!
# Check status docker ps | grep kuno # View logs docker logs kuno # Backup data (from /opt/kuno) cd /opt/kuno sudo tar -czf blog-backup-$(date +%Y%m%d).tar.gz ./blog-data # Stop and remove docker stop kuno docker rm kuno
To upgrade your Docker deployment while preserving all data:
# Create backup directory mkdir -p ./backups/$(date +%Y%m%d_%H%M%S) # Backup the data volume docker run --rm -v blog-data:/data -v $(pwd)/backups/$(date +%Y%m%d_%H%M%S):/backup alpine sh -c "cd /data && tar czf /backup/blog-data-backup.tar.gz ." # Or if using bind mount, simply copy the directory cd /opt/kuno cp -r ./blog-data ./backups/$(date +%Y%m%d_%H%M%S)/
docker pull ictrun/kuno:latest
docker stop kuno docker rm kuno
# If using named volume (recommended) docker run -d \ --name kuno \ --restart unless-stopped \ -p 80:80 \ -v blog-data:/app/data \ -e NEXT_PUBLIC_API_URL=https://your-domain.com/api \ -e DB_PATH=/app/data/blog.db \ ictrun/kuno:latest # If using bind mount cd /opt/kuno docker run -d \ --name kuno \ --restart unless-stopped \ -p 80:80 \ -v /opt/kuno/blog-data:/app/data \ -e NEXT_PUBLIC_API_URL=https://your-domain.com/api \ -e DB_PATH=/app/data/blog.db \ ictrun/kuno:latest
# Check container status docker ps | grep kuno # Check logs for any errors docker logs kuno # Test the application curl -f http://localhost/api/categories || echo "API check failed"
To upgrade your Docker Compose deployment while preserving all data:
# Create backup directory mkdir -p ./backups/$(date +%Y%m%d_%H%M%S) # Stop services temporarily for consistent backup docker-compose stop # Backup the data volume docker run --rm -v blog_blog_data:/data -v $(pwd)/backups/$(date +%Y%m%d_%H%M%S):/backup alpine sh -c "cd /data && tar czf /backup/blog-data-backup.tar.gz ." # Or backup the entire compose environment cp -r ./data ./backups/$(date +%Y%m%d_%H%M%S)/ 2>/dev/null || true cp docker-compose.yml ./backups/$(date +%Y%m%d_%H%M%S)/ 2>/dev/null || true cp .env ./backups/$(date +%Y%m%d_%H%M%S)/ 2>/dev/null || true # Restart services docker-compose start
docker-compose pull
# Method 1: Rolling update (recommended for production) docker-compose up -d --force-recreate --remove-orphans # Method 2: Complete restart (if you need to stop everything) docker-compose down && docker-compose up -d
# Remove unused images to free up space docker image prune -f # Or remove specific old images docker images | grep kuno | grep -v latest | awk '{print 3ドル}' | xargs docker rmi 2>/dev/null || true
# Check all services status docker-compose ps # Check logs for any errors docker-compose logs -f --tail=50 # Test the application curl -f http://localhost:3000 || echo "Frontend check failed" curl -f http://localhost:8080/api/categories || echo "API check failed"
For convenience, you can create an automated upgrade script:
#!/bin/bash # save as upgrade-docker.sh set -e echo "🚀 Starting Docker deployment upgrade..." # Configuration CONTAINER_NAME="kuno" IMAGE_NAME="ictrun/kuno:latest" BACKUP_DIR="./backups/$(date +%Y%m%d_%H%M%S)" # Create backup echo "📦 Creating backup..." mkdir -p "$BACKUP_DIR" docker run --rm -v blog-data:/data -v "$BACKUP_DIR":/backup alpine sh -c "cd /data && tar czf /backup/blog-data-backup.tar.gz ." # Pull latest image echo "⬇️ Pulling latest image..." docker pull "$IMAGE_NAME" # Stop and remove old container echo "🛑 Stopping old container..." docker stop "$CONTAINER_NAME" 2>/dev/null || true docker rm "$CONTAINER_NAME" 2>/dev/null || true # Start new container echo "🚀 Starting new container..." docker run -d \ --name "$CONTAINER_NAME" \ --restart unless-stopped \ -p 80:80 \ -v blog-data:/app/data \ -e NEXT_PUBLIC_API_URL="${NEXT_PUBLIC_API_URL:-http://localhost/api}" \ -e DB_PATH=/app/data/blog.db \ "$IMAGE_NAME" # Verify echo "✅ Verifying upgrade..." sleep 10 if docker ps | grep -q "$CONTAINER_NAME"; then echo "✅ Upgrade completed successfully!" echo "📄 Backup saved to: $BACKUP_DIR" else echo "❌ Upgrade failed! Check logs: docker logs $CONTAINER_NAME" exit 1 fi
#!/bin/bash # save as upgrade-compose.sh set -e echo "🚀 Starting Docker Compose deployment upgrade..." # Configuration BACKUP_DIR="./backups/$(date +%Y%m%d_%H%M%S)" # Create backup echo "📦 Creating backup..." mkdir -p "$BACKUP_DIR" docker-compose stop docker run --rm -v blog_blog_data:/data -v "$BACKUP_DIR":/backup alpine sh -c "cd /data && tar czf /backup/blog-data-backup.tar.gz ." cp -r ./data "$BACKUP_DIR/" 2>/dev/null || true cp docker-compose.yml "$BACKUP_DIR/" 2>/dev/null || true cp .env "$BACKUP_DIR/" 2>/dev/null || true # Pull and upgrade echo "⬇️ Pulling latest images..." docker-compose pull echo "🔄 Upgrading services..." docker-compose up -d --force-recreate --remove-orphans # Clean up echo "🧹 Cleaning up old images..." docker image prune -f # Verify echo "✅ Verifying upgrade..." sleep 15 if docker-compose ps | grep -q "Up"; then echo "✅ Upgrade completed successfully!" echo "📄 Backup saved to: $BACKUP_DIR" else echo "❌ Upgrade failed! Check logs: docker-compose logs" exit 1 fi
If an upgrade fails, you can rollback to the previous version:
# Stop the failed container docker stop kuno && docker rm kuno # Restore from backup (if needed) docker run --rm -v blog-data:/data -v $(pwd)/backups/BACKUP_DATE:/backup alpine sh -c "cd /data && tar xzf /backup/blog-data-backup.tar.gz" # Run the previous image version docker run -d \ --name kuno \ --restart unless-stopped \ -p 80:80 \ -v blog-data:/app/data \ -e NEXT_PUBLIC_API_URL=https://your-domain.com/api \ ictrun/kuno:PREVIOUS_TAG
# Edit docker-compose.yml to use previous image tag # Then restart docker-compose down docker-compose up -d # Restore data if needed docker-compose stop docker run --rm -v blog_blog_data:/data -v $(pwd)/backups/BACKUP_DATE:/backup alpine sh -c "cd /data && tar xzf /backup/blog-data-backup.tar.gz" docker-compose start
- Backup your data before starting
- Test the backup by extracting it to a temporary location
- Note your current version for potential rollback
- Check available disk space for new images
- Plan maintenance window for production systems
- Verify environment variables are correctly set
- Test the application after upgrade
- Monitor logs for any issues
- Update any external monitoring or health checks
- Document the upgrade in your change log
docker-compose up --build -d
docker-compose -f docker-compose.prod.yml up --build -d
- Runtime Configuration: The API URL can be set dynamically at container startup via the
NEXT_PUBLIC_API_URLenvironment variable. - No Rebuild Required: Changes to the API URL only require restarting the container, not rebuilding the image.
- Automatic Detection: The system automatically detects and applies the API URL configuration during container startup.
- Fallback Support: If no environment variable is provided, defaults to
http://localhost:8080/api.
Local Development:
NEXT_PUBLIC_API_URL=http://localhost:8080/api
Production:
NEXT_PUBLIC_API_URL=https://yourdomain.com/api
The frontend will automatically use:
- API requests:
https://yourdomain.com/api/* - Base URL for SEO/metadata:
https://yourdomain.com
blog_data: Persistent storage for SQLite database
./start.sh
./stop.sh
docker-compose logs -f
docker-compose restart
docker-compose down -v docker system prune -f
If you forget the admin password, follow these steps to reset it safely:
docker stop kuno
Navigate to the application directory and run with recovery mode:
cd /opt/kuno docker run -d \ --name kuno_recovery \ --restart unless-stopped \ -p 80:80 \ -v /opt/kuno/blog-data:/app/data \ -e NEXT_PUBLIC_API_URL="http://localhost/api" \ -e DB_PATH="/app/data/blog.db" \ -e RECOVERY_MODE="true" \ ictrun/kuno:latest
The system will:
- Reset the admin password to
xuemian168 - Display the reset credentials in the logs
- Refuse to start for security reasons
# View logs to confirm password reset docker logs kuno_recovery # Remove recovery container docker rm -f kuno_recovery
# Run with normal mode docker run -d \ --name kuno \ --restart unless-stopped \ -p 80:80 \ -v /opt/kuno/blog-data:/app/data \ -e NEXT_PUBLIC_API_URL="http://localhost/api" \ -e DB_PATH="/app/data/blog.db" \ -e RECOVERY_MODE="false" \ ictrun/kuno:latest
- Username:
admin - Password:
xuemian168
- Login to the admin panel at
http://localhost/admin - Go to Settings → Security Settings
- Change your password to a secure one
- Recovery mode requires physical access to the server to modify environment variables
- The system will not start when recovery mode is active - this prevents unauthorized access
- Always disable recovery mode immediately after password reset
- Change the default password immediately after recovery
- Recovery mode is designed for emergency use only
Problem: System won't start after enabling recovery mode Solution: This is intentional. Check the logs to confirm password was reset, then disable recovery mode.
Problem: Recovery mode doesn't reset password
Solution: Ensure the environment variable is set correctly (RECOVERY_MODE=true) and check Docker logs for error messages.
cd backend
go mod download
go run cmd/server/main.gocd frontend
npm install
npm run devcd backend
go build -o bin/server cmd/server/main.gocd frontend
npm run build
npm start- Port conflicts: Make sure ports 3000 and 8080 are available
- Docker permissions: Run with
sudoif needed on Linux - Build failures: Clear Docker cache with
docker system prune -f - Deployment script errors:
- Issue:
curl | bashfails with "syntax error near unexpected token 'fi'" - Solution: Download script first:
curl -sSL https://raw.githubusercontent.com/xuemian168/kuno/main/deploy-from-hub.sh -o deploy.sh && chmod +x deploy.sh && ./deploy.sh - Reason: Interactive scripts require local execution, not piped execution
- Issue:
- API URL issues:
- Issue: Frontend shows "Request URL: http://localhost:8080/api/..." even when
NEXT_PUBLIC_API_URLis set - Solution: Restart the container to apply the new environment variable
- Verification: Check container logs:
docker logs container-nameto see "Setting runtime API URL to: your-url"
- Issue: Frontend shows "Request URL: http://localhost:8080/api/..." even when
The application includes health checks for both services:
- Backend:
http://localhost:8080/api/categories - Frontend:
http://localhost:3000
View service logs:
# All services docker-compose logs -f # Specific service docker-compose logs -f backend docker-compose logs -f frontend
- Use
docker-compose.prod.ymlfor production - Configure proper SSL certificates in the
ssldirectory - Update Nginx configuration for your domain
- Set up backup for the SQLite database
- Configure monitoring and logging
This project is sponsored by TIKHUB.IO
TikHub.io is a provider of high-quality data interface services. It is committed to providing a one-stop overseas social media data API and tool service platform for developers, creators, and enterprises. It faces global users, supports custom extensions, and builds a community-driven ecosystem. Tikhub_LOGO