Add CI/CD workflows for development and production deployments
Some checks failed
Deploy Development / deploy (push) Failing after 29s
Some checks failed
Deploy Development / deploy (push) Failing after 29s
- Created `deploy-dev.yml` for automated deployment to the development server on push to the `dev` branch. - Created `deploy.yml` for automated deployment to the production server on push to the `main` or `master` branches. - Added deployment instructions in `DEPLOYMENT-PORTAINER.md` for using Portainer and Traefik. - Documented Gitea Actions deployment process in `DEPLOYMENT.md`. - Configured Traefik SSL settings in `TRAEFIK-SSL-CONFIG.md` for both development and production environments. - Implemented a deployment script `deploy.sh` for manual deployments. - Added Docker Compose configurations for development (`docker-compose.portainer.dev.yml`) and production (`docker-compose.portainer.yml`) environments. - Updated main `docker-compose.yml` to support Traefik integration and environment variable configurations.
This commit is contained in:
26
.env.example
26
.env.example
@@ -1,25 +1,33 @@
|
||||
APP_ENV=local
|
||||
APP_DEBUG=true
|
||||
APP_KEY=SomeRandomString
|
||||
APP_URL=http://localhost
|
||||
# Application Configuration
|
||||
APP_ENV=production
|
||||
APP_DEBUG=false
|
||||
APP_KEY=base64:YOUR_APP_KEY_HERE
|
||||
APP_URL=https://merchbay-admin.yourdomain.com
|
||||
|
||||
# Database Configuration - External MySQL
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_HOST=your-mysql-host
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=homestead
|
||||
DB_USERNAME=homestead
|
||||
DB_PASSWORD=secret
|
||||
DB_DATABASE=merchbay_admin
|
||||
DB_USERNAME=your-mysql-user
|
||||
DB_PASSWORD=your-mysql-password
|
||||
|
||||
# Traefik Domain Configuration
|
||||
DOMAIN=merchbay-admin.yourdomain.com
|
||||
|
||||
# Cache & Session
|
||||
CACHE_DRIVER=file
|
||||
SESSION_DRIVER=file
|
||||
QUEUE_DRIVER=sync
|
||||
|
||||
# Redis (Optional)
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
# Mail Configuration
|
||||
MAIL_DRIVER=smtp
|
||||
MAIL_HOST=mailtrap.io
|
||||
MAIL_HOST=smtp.mailtrap.io
|
||||
MAIL_PORT=2525
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
|
||||
46
.gitea/workflows/build-push.yml
Normal file
46
.gitea/workflows/build-push.yml
Normal file
@@ -0,0 +1,46 @@
|
||||
name: Build and Push Docker Image
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Login to Docker Registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ${{ secrets.DOCKER_REGISTRY_URL }}
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Extract metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: ${{ secrets.DOCKER_REGISTRY_URL }}/merchbay_admin
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=raw,value=latest
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=registry,ref=${{ secrets.DOCKER_REGISTRY_URL }}/merchbay_admin:buildcache
|
||||
cache-to: type=registry,ref=${{ secrets.DOCKER_REGISTRY_URL }}/merchbay_admin:buildcache,mode=max
|
||||
111
.gitea/workflows/deploy-dev.yml
Normal file
111
.gitea/workflows/deploy-dev.yml
Normal file
@@ -0,0 +1,111 @@
|
||||
name: Deploy Development
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Build Docker Image
|
||||
run: |
|
||||
docker build -t merchbay_admin:dev .
|
||||
|
||||
- name: Save Docker Image
|
||||
run: |
|
||||
docker save merchbay_admin:dev | gzip > merchbay_admin_dev.tar.gz
|
||||
|
||||
- name: Deploy to Development Server via SSH
|
||||
uses: appleboy/scp-action@master
|
||||
with:
|
||||
host: ${{ secrets.DEPLOY_HOST }}
|
||||
username: ${{ secrets.DEPLOY_USER }}
|
||||
key: ${{ secrets.DEPLOY_SSH_KEY }}
|
||||
port: ${{ secrets.DEPLOY_PORT || 22 }}
|
||||
source: "merchbay_admin_dev.tar.gz,docker-compose.yml"
|
||||
target: "/tmp/merchbay_admin_dev_deploy"
|
||||
|
||||
- name: Execute Development Deployment Script
|
||||
uses: appleboy/ssh-action@master
|
||||
with:
|
||||
host: ${{ secrets.DEPLOY_HOST }}
|
||||
username: ${{ secrets.DEPLOY_USER }}
|
||||
key: ${{ secrets.DEPLOY_SSH_KEY }}
|
||||
port: ${{ secrets.DEPLOY_PORT || 22 }}
|
||||
script: |
|
||||
# Set deployment directory for dev
|
||||
DEPLOY_DIR="/var/www/merchbay_admin_dev"
|
||||
|
||||
# Create deployment directory if it doesn't exist
|
||||
mkdir -p $DEPLOY_DIR
|
||||
|
||||
# Load the Docker image
|
||||
cd /tmp/merchbay_admin_dev_deploy
|
||||
docker load < merchbay_admin_dev.tar.gz
|
||||
|
||||
# Copy docker-compose.yml to deployment directory
|
||||
cp docker-compose.yml $DEPLOY_DIR/
|
||||
|
||||
# Navigate to deployment directory
|
||||
cd $DEPLOY_DIR
|
||||
|
||||
# Update environment file for dev
|
||||
cat > .env << EOF
|
||||
APP_ENV=staging
|
||||
APP_DEBUG=false
|
||||
APP_URL=https://dev.merchbay.app
|
||||
DB_HOST=${{ secrets.DEV_DB_HOST }}
|
||||
DB_PORT=${{ secrets.DEV_DB_PORT || 3306 }}
|
||||
DB_DATABASE=${{ secrets.DEV_DB_DATABASE }}
|
||||
DB_USERNAME=${{ secrets.DEV_DB_USERNAME }}
|
||||
DB_PASSWORD=${{ secrets.DEV_DB_PASSWORD }}
|
||||
DOMAIN=dev.merchbay.app
|
||||
EOF
|
||||
|
||||
# Stop existing container
|
||||
docker compose down || true
|
||||
|
||||
# Remove old image
|
||||
docker image prune -f
|
||||
|
||||
# Ensure Traefik network exists
|
||||
docker network inspect traefik-public >/dev/null 2>&1 || docker network create traefik-public
|
||||
|
||||
# Update docker-compose for dev
|
||||
export DOMAIN=dev.merchbay.app
|
||||
export APP_URL=https://dev.merchbay.app
|
||||
|
||||
# Start the application
|
||||
docker compose up -d
|
||||
|
||||
# Wait for container to be ready
|
||||
sleep 10
|
||||
|
||||
# Run migrations
|
||||
docker compose exec -T app php artisan migrate --force
|
||||
|
||||
# Clear and cache configuration
|
||||
docker compose exec -T app php artisan config:cache
|
||||
docker compose exec -T app php artisan route:cache
|
||||
docker compose exec -T app php artisan view:cache
|
||||
|
||||
# Cleanup
|
||||
rm -rf /tmp/merchbay_admin_dev_deploy
|
||||
|
||||
echo "Development deployment completed successfully!"
|
||||
echo "Application available at: https://dev.merchbay.app"
|
||||
|
||||
- name: Health Check
|
||||
run: |
|
||||
sleep 10
|
||||
curl -f https://dev.merchbay.app || exit 1
|
||||
122
.gitea/workflows/deploy.yml
Normal file
122
.gitea/workflows/deploy.yml
Normal file
@@ -0,0 +1,122 @@
|
||||
name: Deploy Production
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
# If using self-hosted runner, change to:
|
||||
# runs-on: self-hosted
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Login to Docker Registry (Optional)
|
||||
if: ${{ secrets.DOCKER_REGISTRY_URL != '' }}
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ${{ secrets.DOCKER_REGISTRY_URL }}
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Build Docker Image
|
||||
run: |
|
||||
docker build -t merchbay_admin:latest .
|
||||
|
||||
- name: Save Docker Image
|
||||
run: |
|
||||
docker save merchbay_admin:latest | gzip > merchbay_admin.tar.gz
|
||||
|
||||
- name: Deploy to Server via SSH
|
||||
uses: appleboy/scp-action@master
|
||||
with:
|
||||
host: ${{ secrets.DEPLOY_HOST }}
|
||||
username: ${{ secrets.DEPLOY_USER }}
|
||||
key: ${{ secrets.DEPLOY_SSH_KEY }}
|
||||
port: ${{ secrets.DEPLOY_PORT || 22 }}
|
||||
source: "merchbay_admin.tar.gz,docker-compose.yml"
|
||||
target: "/tmp/merchbay_admin_deploy"
|
||||
|
||||
- name: Execute Production Deployment Script
|
||||
uses: appleboy/ssh-action@master
|
||||
with:
|
||||
host: ${{ secrets.PROD_DEPLOY_HOST }}
|
||||
username: ${{ secrets.PROD_DEPLOY_USER }}
|
||||
key: ${{ secrets.PROD_DEPLOY_SSH_KEY }}
|
||||
port: ${{ secrets.PROD_DEPLOY_PORT || 22 }}
|
||||
script: |
|
||||
# Set deployment directory for production
|
||||
DEPLOY_DIR="/var/www/merchbay_admin"
|
||||
|
||||
# Create deployment directory if it doesn't exist
|
||||
mkdir -p $DEPLOY_DIR
|
||||
|
||||
# Load the Docker image
|
||||
cd /tmp/merchbay_admin_deploy
|
||||
docker load < merchbay_admin.tar.gz
|
||||
|
||||
# Copy docker-compose.yml to deployment directory
|
||||
cp docker-compose.yml $DEPLOY_DIR/
|
||||
|
||||
# Navigate to deployment directory
|
||||
cd $DEPLOY_DIR
|
||||
|
||||
# Update environment file for production
|
||||
cat > .env << EOF
|
||||
APP_ENV=production
|
||||
APP_DEBUG=false
|
||||
APP_URL=https://merchbay.app
|
||||
DB_HOST=${{ secrets.PROD_DB_HOST }}
|
||||
DB_PORT=${{ secrets.PROD_DB_PORT || 3306 }}
|
||||
DB_DATABASE=${{ secrets.PROD_DB_DATABASE }}
|
||||
DB_USERNAME=${{ secrets.PROD_DB_USERNAME }}
|
||||
DB_PASSWORD=${{ secrets.PROD_DB_PASSWORD }}
|
||||
DOMAIN=merchbay.app
|
||||
EOF
|
||||
|
||||
# Stop existing container (disconnect from Traefik network gracefully)
|
||||
docker compose down || true
|
||||
|
||||
# Remove old image (optional, keeps only latest)
|
||||
docker image prune -f
|
||||
|
||||
# Ensure Traefik network exists
|
||||
docker network inspect traefik-public >/dev/null 2>&1 || docker network create traefik-public
|
||||
|
||||
# Update docker-compose for production
|
||||
export DOMAIN=merchbay.app
|
||||
export APP_URL=https://merchbay.app
|
||||
|
||||
# Start the application (will auto-connect to Traefik with paid SSL)
|
||||
docker compose up -d
|
||||
|
||||
# Wait for container to be ready
|
||||
sleep 10
|
||||
|
||||
# Run migrations
|
||||
docker compose exec -T app php artisan migrate --force
|
||||
|
||||
# Clear and cache configuration
|
||||
docker compose exec -T app php artisan config:cache
|
||||
docker compose exec -T app php artisan route:cache
|
||||
docker compose exec -T app php artisan view:cache
|
||||
|
||||
# Cleanup
|
||||
rm -rf /tmp/merchbay_admin_deploy
|
||||
|
||||
echo "Production deployment completed successfully!"
|
||||
echo "Application available at: https://merchbay.app"
|
||||
|
||||
- name: Health Check
|
||||
run: |
|
||||
sleep 10
|
||||
curl -f https://merchbay.app || exit 1
|
||||
405
DEPLOYMENT-PORTAINER.md
Normal file
405
DEPLOYMENT-PORTAINER.md
Normal file
@@ -0,0 +1,405 @@
|
||||
# Deployment with Portainer and Traefik
|
||||
|
||||
This guide covers deploying MerchBay Admin using your existing Portainer and Traefik setup.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- ✅ Gitea self-hosted with Gitea runners configured
|
||||
- ✅ Portainer installed and accessible
|
||||
- ✅ Traefik reverse proxy running with:
|
||||
- `web` entrypoint (port 80)
|
||||
- `websecure` entrypoint (port 443)
|
||||
- Let's Encrypt certificate resolver named `letsencrypt`
|
||||
- External network named `traefik`
|
||||
|
||||
## Deployment Methods
|
||||
|
||||
### Method 1: Portainer Stack Deployment (Recommended)
|
||||
|
||||
#### Step 1: Prepare the Image
|
||||
|
||||
Build the Docker image via Gitea Actions or manually:
|
||||
|
||||
```bash
|
||||
# Using Gitea Actions (will build automatically on push)
|
||||
git push origin main
|
||||
|
||||
# OR build manually
|
||||
docker build -t merchbay_admin:latest .
|
||||
```
|
||||
|
||||
#### Step 2: Deploy in Portainer
|
||||
|
||||
1. **Access Portainer** → `Stacks` → `Add stack`
|
||||
|
||||
2. **Stack Configuration:**
|
||||
- Name: `merchbay-admin`
|
||||
- Build method: `Web editor`
|
||||
|
||||
3. **Paste the content from `docker-compose.portainer.yml`** and update:
|
||||
- `APP_URL`: Your domain (e.g., `https://merchbay-admin.yourdomain.com`)
|
||||
- `DB_HOST`: Your MySQL host
|
||||
- `DB_DATABASE`: Database name
|
||||
- `DB_USERNAME`: Database username
|
||||
- `DB_PASSWORD`: Database password
|
||||
- Traefik Host rule: Replace `merchbay-admin.yourdomain.com` with your actual domain
|
||||
|
||||
4. **Deploy the stack**
|
||||
|
||||
5. **Run Initial Setup:**
|
||||
- Go to `Containers` → Find `merchbay_admin_app`
|
||||
- Click `Console` → Connect with `/bin/bash`
|
||||
- Run:
|
||||
```bash
|
||||
php artisan migrate --force
|
||||
php artisan config:cache
|
||||
php artisan route:cache
|
||||
```
|
||||
|
||||
### Method 2: Gitea Actions CI/CD (Automated)
|
||||
|
||||
#### Step 1: Configure Gitea Secrets
|
||||
|
||||
In your Gitea repository → `Settings` → `Secrets` → `Actions`, add:
|
||||
|
||||
| Secret Name | Value | Description |
|
||||
|------------|-------|-------------|
|
||||
| `DEPLOY_HOST` | `192.168.1.100` | Your server IP/hostname |
|
||||
| `DEPLOY_USER` | `deploy` | SSH username |
|
||||
| `DEPLOY_SSH_KEY` | `-----BEGIN RSA...` | Private SSH key |
|
||||
| `DEPLOY_PORT` | `22` | SSH port (optional) |
|
||||
| `DEPLOY_DIR` | `/var/www/merchbay_admin` | Deployment directory |
|
||||
| `DOMAIN` | `merchbay-admin.yourdomain.com` | Your domain for Traefik |
|
||||
|
||||
#### Step 2: Prepare Deployment Server
|
||||
|
||||
```bash
|
||||
# SSH into your server
|
||||
ssh deploy@your-server
|
||||
|
||||
# Create deployment directory
|
||||
sudo mkdir -p /var/www/merchbay_admin
|
||||
sudo chown $USER:$USER /var/www/merchbay_admin
|
||||
cd /var/www/merchbay_admin
|
||||
|
||||
# Create .env file
|
||||
nano .env
|
||||
```
|
||||
|
||||
Add to `.env`:
|
||||
```env
|
||||
APP_ENV=production
|
||||
APP_DEBUG=false
|
||||
APP_URL=https://merchbay-admin.yourdomain.com
|
||||
DB_HOST=your-mysql-host
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=merchbay_admin
|
||||
DB_USERNAME=your-mysql-user
|
||||
DB_PASSWORD=your-mysql-password
|
||||
DOMAIN=merchbay-admin.yourdomain.com
|
||||
```
|
||||
|
||||
#### Step 3: Ensure Traefik Network Exists
|
||||
|
||||
```bash
|
||||
docker network inspect traefik >/dev/null 2>&1 || docker network create traefik
|
||||
```
|
||||
|
||||
#### Step 4: Deploy
|
||||
|
||||
Simply push to your main branch:
|
||||
|
||||
```bash
|
||||
git add .
|
||||
git commit -m "Deploy application"
|
||||
git push origin main
|
||||
```
|
||||
|
||||
Gitea Actions will automatically:
|
||||
1. Build the Docker image
|
||||
2. Transfer to your server
|
||||
3. Deploy with docker-compose
|
||||
4. Connect to Traefik network
|
||||
5. Run migrations
|
||||
6. Cache configurations
|
||||
|
||||
### Method 3: Manual Deployment with Script
|
||||
|
||||
```bash
|
||||
# On your server
|
||||
cd /var/www/merchbay_admin
|
||||
|
||||
# Pull latest code
|
||||
git pull origin main
|
||||
|
||||
# Run deployment script
|
||||
./deploy.sh
|
||||
```
|
||||
|
||||
## Traefik Configuration
|
||||
|
||||
### Verify Traefik Setup
|
||||
|
||||
Ensure your Traefik configuration includes:
|
||||
|
||||
```yaml
|
||||
# traefik.yml or docker-compose.yml
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
websecure:
|
||||
address: ":443"
|
||||
|
||||
certificatesResolvers:
|
||||
letsencrypt:
|
||||
acme:
|
||||
email: your-email@example.com
|
||||
storage: /letsencrypt/acme.json
|
||||
httpChallenge:
|
||||
entryPoint: web
|
||||
```
|
||||
|
||||
### External Network
|
||||
|
||||
Ensure Traefik network exists and is external:
|
||||
|
||||
```bash
|
||||
# Create network if it doesn't exist
|
||||
docker network create traefik
|
||||
|
||||
# Verify
|
||||
docker network inspect traefik
|
||||
```
|
||||
|
||||
## DNS Configuration
|
||||
|
||||
Point your domain to your server:
|
||||
|
||||
```
|
||||
Type: A Record
|
||||
Name: merchbay-admin (or @ for root domain)
|
||||
Value: YOUR_SERVER_IP
|
||||
TTL: 3600
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
### 1. Check Container Status
|
||||
|
||||
**Via Portainer:**
|
||||
- Navigate to `Containers`
|
||||
- Verify `merchbay_admin_app` is running
|
||||
|
||||
**Via CLI:**
|
||||
```bash
|
||||
docker ps | grep merchbay_admin
|
||||
```
|
||||
|
||||
### 2. Check Traefik Dashboard
|
||||
|
||||
If you have Traefik dashboard enabled:
|
||||
- Look for `merchbay-admin@docker` router
|
||||
- Verify it's connected to the correct service
|
||||
|
||||
### 3. Test Application
|
||||
|
||||
```bash
|
||||
# Test HTTPS
|
||||
curl -I https://merchbay-admin.yourdomain.com
|
||||
|
||||
# Should return: HTTP/2 200
|
||||
```
|
||||
|
||||
### 4. Check Logs
|
||||
|
||||
**Via Portainer:**
|
||||
- `Containers` → `merchbay_admin_app` → `Logs`
|
||||
|
||||
**Via CLI:**
|
||||
```bash
|
||||
docker logs merchbay_admin_app -f
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Issue: Container not accessible via domain
|
||||
|
||||
**Check Traefik labels:**
|
||||
```bash
|
||||
docker inspect merchbay_admin_app | grep -A 20 Labels
|
||||
```
|
||||
|
||||
**Verify network connection:**
|
||||
```bash
|
||||
docker network inspect traefik | grep merchbay_admin
|
||||
```
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
# Reconnect to Traefik network
|
||||
docker network connect traefik merchbay_admin_app
|
||||
```
|
||||
|
||||
### Issue: SSL certificate not generating
|
||||
|
||||
**Check Traefik logs:**
|
||||
```bash
|
||||
docker logs traefik | grep letsencrypt
|
||||
```
|
||||
|
||||
**Common fixes:**
|
||||
1. Ensure port 80 is accessible (Let's Encrypt HTTP challenge)
|
||||
2. Verify DNS is pointing to your server
|
||||
3. Check email in Traefik's ACME configuration
|
||||
4. Ensure `acme.json` has correct permissions (600)
|
||||
|
||||
### Issue: Database connection failed
|
||||
|
||||
**Check environment variables:**
|
||||
```bash
|
||||
docker exec merchbay_admin_app env | grep DB_
|
||||
```
|
||||
|
||||
**Test connection:**
|
||||
```bash
|
||||
docker exec merchbay_admin_app php artisan tinker
|
||||
>>> DB::connection()->getPdo();
|
||||
```
|
||||
|
||||
### Issue: 502 Bad Gateway
|
||||
|
||||
**Possible causes:**
|
||||
1. Application not fully started
|
||||
2. Wrong port in Traefik label (should be 80)
|
||||
3. Application crashed
|
||||
|
||||
**Check:**
|
||||
```bash
|
||||
# Container status
|
||||
docker ps -a | grep merchbay_admin
|
||||
|
||||
# Application logs
|
||||
docker logs merchbay_admin_app --tail 100
|
||||
|
||||
# Restart container
|
||||
docker restart merchbay_admin_app
|
||||
```
|
||||
|
||||
## Updating the Application
|
||||
|
||||
### Via Gitea Actions (Automatic)
|
||||
|
||||
```bash
|
||||
git add .
|
||||
git commit -m "Update application"
|
||||
git push origin main
|
||||
```
|
||||
|
||||
### Via Portainer
|
||||
|
||||
1. Go to `Stacks` → `merchbay-admin`
|
||||
2. Click `Editor`
|
||||
3. Update image or configuration
|
||||
4. Click `Update the stack`
|
||||
5. Enable "Re-pull image and redeploy"
|
||||
|
||||
### Manual Update
|
||||
|
||||
```bash
|
||||
ssh deploy@your-server
|
||||
cd /var/www/merchbay_admin
|
||||
./deploy.sh
|
||||
```
|
||||
|
||||
## Rollback
|
||||
|
||||
### Via Portainer
|
||||
|
||||
1. Go to `Stacks` → `merchbay-admin`
|
||||
2. Click on the stack
|
||||
3. Find previous version in `Stack History` (if available)
|
||||
4. Revert to previous version
|
||||
|
||||
### Manual Rollback
|
||||
|
||||
```bash
|
||||
# List available backups
|
||||
ls -lh /var/www/merchbay_admin/backups/
|
||||
|
||||
# Restore from backup
|
||||
cd /var/www/merchbay_admin
|
||||
tar -xzf backups/backup_YYYYMMDD_HHMMSS.tar.gz
|
||||
|
||||
# Restart
|
||||
docker compose restart
|
||||
```
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
1. **Use Strong Database Passwords**: Generate with `openssl rand -base64 32`
|
||||
2. **Restrict SSH Access**: Use key-based authentication only
|
||||
3. **Firewall Rules**: Only allow necessary ports (80, 443, 22)
|
||||
4. **Regular Backups**: Automated backups of database and storage
|
||||
5. **Keep Docker Updated**: Regularly update Docker and images
|
||||
6. **Monitor Logs**: Set up log monitoring/alerting
|
||||
7. **Use Secrets**: Never commit sensitive data to repository
|
||||
8. **HTTPS Only**: Ensure HTTP redirects to HTTPS
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### PHP-FPM Configuration
|
||||
|
||||
Consider switching to PHP-FPM for better performance:
|
||||
|
||||
```dockerfile
|
||||
FROM php:7.4-fpm-alpine
|
||||
# ... additional configuration
|
||||
```
|
||||
|
||||
### Use Redis for Cache/Sessions
|
||||
|
||||
```env
|
||||
CACHE_DRIVER=redis
|
||||
SESSION_DRIVER=redis
|
||||
REDIS_HOST=redis
|
||||
```
|
||||
|
||||
Add Redis service to docker-compose:
|
||||
```yaml
|
||||
services:
|
||||
redis:
|
||||
image: redis:alpine
|
||||
networks:
|
||||
- default
|
||||
```
|
||||
|
||||
### Enable OPcache
|
||||
|
||||
Already included in Dockerfile, verify:
|
||||
```bash
|
||||
docker exec merchbay_admin_app php -i | grep opcache
|
||||
```
|
||||
|
||||
## Monitoring
|
||||
|
||||
### View Real-time Logs in Portainer
|
||||
|
||||
1. Navigate to `Containers`
|
||||
2. Click on `merchbay_admin_app`
|
||||
3. Select `Logs` tab
|
||||
4. Enable `Auto-refresh`
|
||||
|
||||
### Set Up Alerts
|
||||
|
||||
Configure Portainer notifications:
|
||||
1. `Settings` → `Notifications`
|
||||
2. Add webhook or email notification
|
||||
3. Set up container health checks
|
||||
|
||||
## Support
|
||||
|
||||
For issues:
|
||||
1. Check application logs in Portainer
|
||||
2. Verify Traefik configuration
|
||||
3. Test database connectivity
|
||||
4. Review Gitea Actions logs if using CI/CD
|
||||
264
DEPLOYMENT.md
Normal file
264
DEPLOYMENT.md
Normal file
@@ -0,0 +1,264 @@
|
||||
# Gitea Actions Deployment Guide
|
||||
|
||||
This repository uses Gitea Actions for automated deployment to your server.
|
||||
|
||||
## Workflows
|
||||
|
||||
### 1. Deploy Workflow (`.gitea/workflows/deploy.yml`)
|
||||
Automatically deploys the application when code is pushed to `main` or `master` branch.
|
||||
|
||||
**Steps:**
|
||||
1. Builds Docker image
|
||||
2. Transfers image to deployment server
|
||||
3. Stops existing container
|
||||
4. Starts new container
|
||||
5. Runs database migrations
|
||||
6. Clears and caches Laravel configuration
|
||||
|
||||
### 2. Build and Push Workflow (`.gitea/workflows/build-push.yml`)
|
||||
Builds and pushes Docker images to a registry when a version tag is created.
|
||||
|
||||
## Required Secrets
|
||||
|
||||
Configure these secrets in your Gitea repository settings:
|
||||
`Settings` → `Secrets` → `Actions`
|
||||
|
||||
### Deployment Secrets
|
||||
|
||||
| Secret Name | Description | Example |
|
||||
|------------|-------------|---------|
|
||||
| `DEPLOY_HOST` | Deployment server hostname or IP | `192.168.1.100` or `example.com` |
|
||||
| `DEPLOY_USER` | SSH username for deployment | `deploy` or `ubuntu` |
|
||||
| `DEPLOY_SSH_KEY` | Private SSH key for authentication | `-----BEGIN RSA PRIVATE KEY-----...` |
|
||||
| `DEPLOY_PORT` | SSH port (optional, defaults to 22) | `22` |
|
||||
| `DEPLOY_DIR` | Deployment directory (optional) | `/var/www/merchbay_admin` |
|
||||
|
||||
### Docker Registry Secrets (Optional)
|
||||
|
||||
Only required if using the build-push workflow or private registry:
|
||||
|
||||
| Secret Name | Description | Example |
|
||||
|------------|-------------|---------|
|
||||
| `DOCKER_REGISTRY_URL` | Docker registry URL | `registry.example.com` or `docker.io` |
|
||||
| `DOCKER_USERNAME` | Registry username | `myuser` |
|
||||
| `DOCKER_PASSWORD` | Registry password or token | `mypassword` |
|
||||
|
||||
### Database Configuration on Server
|
||||
|
||||
Create a `.env` file in your deployment directory with database credentials:
|
||||
|
||||
```bash
|
||||
# On your deployment server
|
||||
sudo mkdir -p /var/www/merchbay_admin
|
||||
sudo nano /var/www/merchbay_admin/.env
|
||||
```
|
||||
|
||||
Add your database configuration:
|
||||
|
||||
```env
|
||||
DB_HOST=your-mysql-host
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=merchbay_admin
|
||||
DB_USERNAME=your-mysql-user
|
||||
DB_PASSWORD=your-mysql-password
|
||||
APP_ENV=production
|
||||
APP_DEBUG=false
|
||||
APP_KEY=base64:YOUR_APP_KEY_HERE
|
||||
```
|
||||
|
||||
## Setup Instructions
|
||||
|
||||
### 1. Generate SSH Key for Deployment
|
||||
|
||||
On your local machine or CI server:
|
||||
|
||||
```bash
|
||||
# Generate a new SSH key pair
|
||||
ssh-keygen -t rsa -b 4096 -f ~/.ssh/deploy_key -N ""
|
||||
|
||||
# Copy the public key to your deployment server
|
||||
ssh-copy-id -i ~/.ssh/deploy_key.pub user@your-server
|
||||
|
||||
# Copy the private key content for Gitea secret
|
||||
cat ~/.ssh/deploy_key
|
||||
```
|
||||
|
||||
### 2. Configure Gitea Secrets
|
||||
|
||||
1. Go to your Gitea repository
|
||||
2. Navigate to `Settings` → `Secrets` → `Actions`
|
||||
3. Add each required secret listed above
|
||||
4. For `DEPLOY_SSH_KEY`, paste the entire private key content
|
||||
|
||||
### 3. Prepare Deployment Server
|
||||
|
||||
On your deployment server, install Docker and Docker Compose:
|
||||
|
||||
```bash
|
||||
# Install Docker
|
||||
curl -fsSL https://get.docker.com -o get-docker.sh
|
||||
sudo sh get-docker.sh
|
||||
|
||||
# Add your user to docker group
|
||||
sudo usermod -aG docker $USER
|
||||
|
||||
# Install Docker Compose
|
||||
sudo apt-get update
|
||||
sudo apt-get install docker-compose-plugin
|
||||
|
||||
# Create deployment directory
|
||||
sudo mkdir -p /var/www/merchbay_admin
|
||||
sudo chown $USER:$USER /var/www/merchbay_admin
|
||||
|
||||
# Create .env file with database credentials
|
||||
nano /var/www/merchbay_admin/.env
|
||||
```
|
||||
|
||||
### 4. Update docker-compose.yml for Production
|
||||
|
||||
Ensure your `docker-compose.yml` references the `.env` file:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
environment:
|
||||
- DB_HOST=${DB_HOST}
|
||||
- DB_PORT=${DB_PORT}
|
||||
- DB_DATABASE=${DB_DATABASE}
|
||||
- DB_USERNAME=${DB_USERNAME}
|
||||
- DB_PASSWORD=${DB_PASSWORD}
|
||||
```
|
||||
|
||||
## Triggering Deployment
|
||||
|
||||
### Automatic Deployment
|
||||
|
||||
Push to main/master branch:
|
||||
|
||||
```bash
|
||||
git add .
|
||||
git commit -m "Deploy updates"
|
||||
git push origin main
|
||||
```
|
||||
|
||||
### Manual Deployment
|
||||
|
||||
1. Go to your Gitea repository
|
||||
2. Click on `Actions`
|
||||
3. Select `Deploy MerchBay Admin` workflow
|
||||
4. Click `Run workflow`
|
||||
|
||||
## Monitoring Deployment
|
||||
|
||||
### View Workflow Logs
|
||||
|
||||
1. Go to `Actions` tab in your Gitea repository
|
||||
2. Click on the running/completed workflow
|
||||
3. View logs for each step
|
||||
|
||||
### Check Application Logs
|
||||
|
||||
On your deployment server:
|
||||
|
||||
```bash
|
||||
cd /var/www/merchbay_admin
|
||||
docker compose logs -f app
|
||||
```
|
||||
|
||||
### Verify Deployment
|
||||
|
||||
```bash
|
||||
# Check container status
|
||||
docker compose ps
|
||||
|
||||
# Test application
|
||||
curl http://localhost:8080
|
||||
|
||||
# Access application shell
|
||||
docker compose exec app bash
|
||||
```
|
||||
|
||||
## Rollback Procedure
|
||||
|
||||
If deployment fails, you can quickly rollback:
|
||||
|
||||
```bash
|
||||
# On deployment server
|
||||
cd /var/www/merchbay_admin
|
||||
|
||||
# Stop current container
|
||||
docker compose down
|
||||
|
||||
# Load previous image (if available)
|
||||
docker images # Find previous image ID
|
||||
docker tag <previous-image-id> merchbay_admin:latest
|
||||
|
||||
# Start with previous version
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### SSH Connection Issues
|
||||
|
||||
```bash
|
||||
# Test SSH connection from CI to server
|
||||
ssh -i ~/.ssh/deploy_key user@your-server
|
||||
|
||||
# Check SSH key permissions
|
||||
chmod 600 ~/.ssh/deploy_key
|
||||
```
|
||||
|
||||
### Docker Permission Issues
|
||||
|
||||
```bash
|
||||
# On deployment server, ensure user is in docker group
|
||||
sudo usermod -aG docker $USER
|
||||
newgrp docker
|
||||
```
|
||||
|
||||
### Migration Failures
|
||||
|
||||
```bash
|
||||
# Manually run migrations
|
||||
docker compose exec app php artisan migrate --force
|
||||
|
||||
# Check database connection
|
||||
docker compose exec app php artisan tinker
|
||||
>>> DB::connection()->getPdo();
|
||||
```
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
1. **Use SSH keys, not passwords** for server authentication
|
||||
2. **Restrict SSH key** to only deployment commands if possible
|
||||
3. **Use secrets** for all sensitive data, never commit to repository
|
||||
4. **Set proper file permissions** on deployment server (755 for directories, 644 for files)
|
||||
5. **Enable firewall** on deployment server and restrict access
|
||||
6. **Use HTTPS** with SSL certificates in production
|
||||
7. **Regular backups** of database and uploaded files
|
||||
|
||||
## Advanced Configuration
|
||||
|
||||
### Using Docker Registry
|
||||
|
||||
To use a private registry:
|
||||
|
||||
1. Add registry secrets to Gitea
|
||||
2. Update deployment script to pull from registry instead of transferring image
|
||||
3. Use the build-push workflow to automate image publishing
|
||||
|
||||
### Zero-Downtime Deployment
|
||||
|
||||
For zero-downtime deployments, consider:
|
||||
|
||||
1. Using a load balancer
|
||||
2. Running multiple container instances
|
||||
3. Implementing blue-green deployment strategy
|
||||
|
||||
### Environment-Specific Deployments
|
||||
|
||||
Create separate workflows for staging and production:
|
||||
|
||||
- `.gitea/workflows/deploy-staging.yml` (triggered on `develop` branch)
|
||||
- `.gitea/workflows/deploy-production.yml` (triggered on `main` branch)
|
||||
316
TRAEFIK-SSL-CONFIG.md
Normal file
316
TRAEFIK-SSL-CONFIG.md
Normal file
@@ -0,0 +1,316 @@
|
||||
# Traefik SSL Configuration for MerchBay Admin
|
||||
|
||||
This document explains how to configure paid SSL certificates for production and automatic Let's Encrypt for development.
|
||||
|
||||
## Network Configuration
|
||||
|
||||
All deployments use the external network: `traefik-public`
|
||||
|
||||
```bash
|
||||
# Ensure the network exists
|
||||
docker network inspect traefik-public >/dev/null 2>&1 || docker network create traefik-public
|
||||
```
|
||||
|
||||
## Development (dev.merchbay.app) - Automatic SSL
|
||||
|
||||
Development uses Let's Encrypt for automatic SSL certificate generation.
|
||||
|
||||
### Traefik Configuration
|
||||
|
||||
Ensure your Traefik has Let's Encrypt configured:
|
||||
|
||||
```yaml
|
||||
# traefik.yml or dynamic config
|
||||
certificatesResolvers:
|
||||
letsencrypt:
|
||||
acme:
|
||||
email: admin@merchbay.app
|
||||
storage: /letsencrypt/acme.json
|
||||
httpChallenge:
|
||||
entryPoint: web
|
||||
```
|
||||
|
||||
### Application Labels (Already configured in docker-compose)
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
- "traefik.http.routers.merchbay-admin-dev.tls.certresolver=letsencrypt"
|
||||
```
|
||||
|
||||
## Production (merchbay.app) - Paid SSL Certificate
|
||||
|
||||
Production uses a paid SSL certificate (e.g., from GoDaddy, Namecheap, Cloudflare).
|
||||
|
||||
### Step 1: Prepare SSL Certificate Files
|
||||
|
||||
You should have these files from your SSL provider:
|
||||
- `merchbay.app.crt` - Certificate file
|
||||
- `merchbay.app.key` - Private key file
|
||||
- `ca-bundle.crt` - CA bundle (optional, for chain)
|
||||
|
||||
Create a combined certificate file:
|
||||
|
||||
```bash
|
||||
# Create SSL directory in Traefik
|
||||
mkdir -p /opt/traefik/certs
|
||||
|
||||
# Copy your certificate and key
|
||||
sudo cp merchbay.app.crt /opt/traefik/certs/
|
||||
sudo cp merchbay.app.key /opt/traefik/certs/
|
||||
|
||||
# If you have a CA bundle, create a full chain
|
||||
cat merchbay.app.crt ca-bundle.crt > /opt/traefik/certs/merchbay.app-fullchain.crt
|
||||
|
||||
# Set proper permissions
|
||||
sudo chmod 600 /opt/traefik/certs/*.key
|
||||
sudo chmod 644 /opt/traefik/certs/*.crt
|
||||
```
|
||||
|
||||
### Step 2: Configure Traefik File Provider
|
||||
|
||||
Create a dynamic configuration file for Traefik:
|
||||
|
||||
```bash
|
||||
sudo nano /opt/traefik/dynamic/certs.yml
|
||||
```
|
||||
|
||||
Add:
|
||||
|
||||
```yaml
|
||||
# /opt/traefik/dynamic/certs.yml
|
||||
tls:
|
||||
certificates:
|
||||
- certFile: /certs/merchbay.app-fullchain.crt
|
||||
keyFile: /certs/merchbay.app.key
|
||||
stores:
|
||||
- default
|
||||
stores:
|
||||
default:
|
||||
defaultCertificate:
|
||||
certFile: /certs/merchbay.app-fullchain.crt
|
||||
keyFile: /certs/merchbay.app.key
|
||||
```
|
||||
|
||||
### Step 3: Update Traefik docker-compose.yml
|
||||
|
||||
Ensure Traefik has file provider enabled and certificates mounted:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v2.10
|
||||
command:
|
||||
- "--providers.docker=true"
|
||||
- "--providers.docker.network=traefik-public"
|
||||
- "--providers.file.directory=/etc/traefik/dynamic"
|
||||
- "--providers.file.watch=true"
|
||||
- "--entrypoints.web.address=:80"
|
||||
- "--entrypoints.websecure.address=:443"
|
||||
# Let's Encrypt for dev
|
||||
- "--certificatesresolvers.letsencrypt.acme.email=admin@merchbay.app"
|
||||
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
|
||||
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- /opt/traefik/certs:/certs:ro
|
||||
- /opt/traefik/dynamic:/etc/traefik/dynamic:ro
|
||||
- traefik-letsencrypt:/letsencrypt
|
||||
networks:
|
||||
- traefik-public
|
||||
```
|
||||
|
||||
### Step 4: Restart Traefik
|
||||
|
||||
```bash
|
||||
cd /path/to/traefik
|
||||
docker compose restart traefik
|
||||
|
||||
# Verify certificates are loaded
|
||||
docker compose logs traefik | grep -i cert
|
||||
```
|
||||
|
||||
## Application Configuration
|
||||
|
||||
### Development Branch (dev)
|
||||
|
||||
File: `docker-compose.portainer.dev.yml`
|
||||
|
||||
- Domain: `dev.merchbay.app`
|
||||
- SSL: Let's Encrypt (automatic)
|
||||
- Certificate Resolver: `letsencrypt`
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
- "traefik.http.routers.merchbay-admin-dev.rule=Host(`dev.merchbay.app`)"
|
||||
- "traefik.http.routers.merchbay-admin-dev.tls.certresolver=letsencrypt"
|
||||
```
|
||||
|
||||
### Production Branch (main)
|
||||
|
||||
File: `docker-compose.portainer.yml`
|
||||
|
||||
- Domain: `merchbay.app`
|
||||
- SSL: Paid certificate (via Traefik file provider)
|
||||
- No certresolver label (uses default store)
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
- "traefik.http.routers.merchbay-admin.rule=Host(`merchbay.app`)"
|
||||
- "traefik.http.routers.merchbay-admin.tls=true"
|
||||
# No certresolver - uses file provider certificate
|
||||
```
|
||||
|
||||
## Gitea Secrets Configuration
|
||||
|
||||
### Development Secrets
|
||||
|
||||
```
|
||||
DEV_DB_HOST=dev-mysql-host
|
||||
DEV_DB_PORT=3306
|
||||
DEV_DB_DATABASE=merchbay_admin_dev
|
||||
DEV_DB_USERNAME=dev_user
|
||||
DEV_DB_PASSWORD=dev_password
|
||||
```
|
||||
|
||||
### Production Secrets
|
||||
|
||||
```
|
||||
PROD_DEPLOY_HOST=prod-server-ip
|
||||
PROD_DEPLOY_USER=deploy
|
||||
PROD_DEPLOY_SSH_KEY=-----BEGIN RSA PRIVATE KEY-----...
|
||||
PROD_DEPLOY_PORT=22
|
||||
PROD_DB_HOST=prod-mysql-host
|
||||
PROD_DB_PORT=3306
|
||||
PROD_DB_DATABASE=merchbay_admin
|
||||
PROD_DB_USERNAME=prod_user
|
||||
PROD_DB_PASSWORD=prod_password
|
||||
```
|
||||
|
||||
### Shared Secrets (if using same server)
|
||||
|
||||
```
|
||||
DEPLOY_HOST=your-server-ip
|
||||
DEPLOY_USER=deploy
|
||||
DEPLOY_SSH_KEY=-----BEGIN RSA PRIVATE KEY-----...
|
||||
DEPLOY_PORT=22
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
### Check Development SSL
|
||||
|
||||
```bash
|
||||
# Check certificate issuer (should be Let's Encrypt)
|
||||
echo | openssl s_client -servername dev.merchbay.app -connect dev.merchbay.app:443 2>/dev/null | openssl x509 -noout -issuer
|
||||
# Should show: issuer=C = US, O = Let's Encrypt, CN = R3
|
||||
```
|
||||
|
||||
### Check Production SSL
|
||||
|
||||
```bash
|
||||
# Check certificate issuer (should be your SSL provider)
|
||||
echo | openssl s_client -servername merchbay.app -connect merchbay.app:443 2>/dev/null | openssl x509 -noout -issuer
|
||||
# Should show your paid SSL provider
|
||||
|
||||
# Check certificate validity
|
||||
echo | openssl s_client -servername merchbay.app -connect merchbay.app:443 2>/dev/null | openssl x509 -noout -dates
|
||||
```
|
||||
|
||||
### Verify in Browser
|
||||
|
||||
1. Visit https://dev.merchbay.app
|
||||
- Certificate should be issued by "Let's Encrypt Authority X3"
|
||||
|
||||
2. Visit https://merchbay.app
|
||||
- Certificate should be issued by your paid SSL provider
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Development SSL Not Working
|
||||
|
||||
```bash
|
||||
# Check Let's Encrypt logs
|
||||
docker logs traefik | grep letsencrypt
|
||||
|
||||
# Verify acme.json permissions
|
||||
ls -l /path/to/letsencrypt/acme.json
|
||||
# Should be: -rw------- (600)
|
||||
|
||||
# Check DNS
|
||||
dig dev.merchbay.app +short
|
||||
# Should return your server IP
|
||||
```
|
||||
|
||||
### Production SSL Not Working
|
||||
|
||||
```bash
|
||||
# Verify Traefik can read certificates
|
||||
docker exec traefik ls -l /certs/
|
||||
|
||||
# Check dynamic configuration is loaded
|
||||
docker exec traefik cat /etc/traefik/dynamic/certs.yml
|
||||
|
||||
# Verify certificate format
|
||||
openssl x509 -in /opt/traefik/certs/merchbay.app-fullchain.crt -text -noout
|
||||
|
||||
# Check private key
|
||||
openssl rsa -in /opt/traefik/certs/merchbay.app.key -check
|
||||
```
|
||||
|
||||
### Certificate Mismatch
|
||||
|
||||
```bash
|
||||
# Verify certificate and key match
|
||||
openssl x509 -noout -modulus -in /opt/traefik/certs/merchbay.app.crt | openssl md5
|
||||
openssl rsa -noout -modulus -in /opt/traefik/certs/merchbay.app.key | openssl md5
|
||||
# Both should output the same hash
|
||||
```
|
||||
|
||||
## Renewing Certificates
|
||||
|
||||
### Development (Let's Encrypt)
|
||||
|
||||
Automatic renewal every 60 days. No action needed.
|
||||
|
||||
### Production (Paid SSL)
|
||||
|
||||
Before certificate expiration:
|
||||
|
||||
1. Download new certificate from your SSL provider
|
||||
2. Update files in `/opt/traefik/certs/`
|
||||
3. Restart Traefik: `docker compose restart traefik`
|
||||
4. Verify: `curl -vI https://merchbay.app`
|
||||
|
||||
## DNS Configuration
|
||||
|
||||
### Development
|
||||
|
||||
```
|
||||
Type: A
|
||||
Name: dev.merchbay.app
|
||||
Value: YOUR_SERVER_IP
|
||||
TTL: 3600
|
||||
```
|
||||
|
||||
### Production
|
||||
|
||||
```
|
||||
Type: A
|
||||
Name: merchbay.app (or @)
|
||||
Value: YOUR_SERVER_IP
|
||||
TTL: 3600
|
||||
|
||||
Type: A
|
||||
Name: www.merchbay.app
|
||||
Value: YOUR_SERVER_IP
|
||||
TTL: 3600
|
||||
```
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
1. **Keep certificates private**: Never commit SSL keys to git
|
||||
2. **Use strong permissions**: 600 for private keys, 644 for certificates
|
||||
3. **Monitor expiration**: Set reminders 30 days before expiration
|
||||
4. **Use HSTS**: Add header after SSL is working correctly
|
||||
5. **Enable OCSP stapling**: Improves SSL performance
|
||||
6. **Regular updates**: Keep Traefik updated for security patches
|
||||
105
deploy.sh
Executable file
105
deploy.sh
Executable file
@@ -0,0 +1,105 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Deployment script for MerchBay Admin
|
||||
# This can be used for manual deployments or called from CI/CD
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
DEPLOY_DIR="${DEPLOY_DIR:-/var/www/merchbay_admin}"
|
||||
APP_NAME="merchbay_admin"
|
||||
BACKUP_DIR="${DEPLOY_DIR}/backups"
|
||||
|
||||
echo -e "${GREEN}Starting deployment of ${APP_NAME}...${NC}"
|
||||
|
||||
# Create backup directory
|
||||
mkdir -p "${BACKUP_DIR}"
|
||||
|
||||
# Backup current state
|
||||
if [ -d "${DEPLOY_DIR}/storage" ]; then
|
||||
echo -e "${YELLOW}Creating backup...${NC}"
|
||||
BACKUP_FILE="${BACKUP_DIR}/backup_$(date +%Y%m%d_%H%M%S).tar.gz"
|
||||
tar -czf "${BACKUP_FILE}" -C "${DEPLOY_DIR}" storage .env 2>/dev/null || true
|
||||
echo -e "${GREEN}Backup created: ${BACKUP_FILE}${NC}"
|
||||
fi
|
||||
|
||||
# Navigate to deployment directory
|
||||
cd "${DEPLOY_DIR}"
|
||||
|
||||
# Check if .env exists
|
||||
if [ ! -f .env ]; then
|
||||
echo -e "${RED}Error: .env file not found!${NC}"
|
||||
echo "Please create .env file with database credentials"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Pull latest code (if using git deployment)
|
||||
if [ -d .git ]; then
|
||||
echo -e "${YELLOW}Pulling latest code...${NC}"
|
||||
git pull origin main || git pull origin master
|
||||
fi
|
||||
|
||||
# Stop existing containers
|
||||
echo -e "${YELLOW}Stopping existing containers...${NC}"
|
||||
docker compose down
|
||||
|
||||
# Build new image
|
||||
echo -e "${YELLOW}Building Docker image...${NC}"
|
||||
docker compose build --no-cache
|
||||
|
||||
# Start containers
|
||||
echo -e "${YELLOW}Starting containers...${NC}"
|
||||
docker compose up -d
|
||||
|
||||
# Wait for container to be ready
|
||||
echo -e "${YELLOW}Waiting for application to start...${NC}"
|
||||
sleep 10
|
||||
|
||||
# Run migrations
|
||||
echo -e "${YELLOW}Running database migrations...${NC}"
|
||||
docker compose exec -T app php artisan migrate --force || {
|
||||
echo -e "${RED}Migration failed! Rolling back...${NC}"
|
||||
docker compose down
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Clear and cache configuration
|
||||
echo -e "${YELLOW}Optimizing application...${NC}"
|
||||
docker compose exec -T app php artisan config:cache
|
||||
docker compose exec -T app php artisan route:cache
|
||||
docker compose exec -T app php artisan view:cache
|
||||
|
||||
# Set proper permissions
|
||||
echo -e "${YELLOW}Setting permissions...${NC}"
|
||||
docker compose exec -T app chown -R www-data:www-data /var/www/html/storage
|
||||
docker compose exec -T app chmod -R 755 /var/www/html/storage
|
||||
|
||||
# Health check
|
||||
echo -e "${YELLOW}Performing health check...${NC}"
|
||||
sleep 5
|
||||
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080)
|
||||
|
||||
if [ "$HTTP_STATUS" -eq 200 ]; then
|
||||
echo -e "${GREEN}✓ Deployment successful! Application is running.${NC}"
|
||||
echo -e "${GREEN}✓ HTTP Status: ${HTTP_STATUS}${NC}"
|
||||
|
||||
# Keep only last 5 backups
|
||||
cd "${BACKUP_DIR}"
|
||||
ls -t backup_*.tar.gz 2>/dev/null | tail -n +6 | xargs -r rm
|
||||
|
||||
echo -e "${GREEN}Deployment completed successfully!${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Health check failed! HTTP Status: ${HTTP_STATUS}${NC}"
|
||||
echo -e "${YELLOW}Check logs: docker compose logs app${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Display container status
|
||||
echo -e "${YELLOW}Container status:${NC}"
|
||||
docker compose ps
|
||||
48
docker-compose.portainer.dev.yml
Normal file
48
docker-compose.portainer.dev.yml
Normal file
@@ -0,0 +1,48 @@
|
||||
version: '3.8'
|
||||
|
||||
# Development Stack - Portainer Configuration
|
||||
# Deploy this through Portainer UI: Stacks -> Add Stack -> Web Editor
|
||||
# Branch: dev
|
||||
|
||||
services:
|
||||
app:
|
||||
image: merchbay_admin:dev
|
||||
container_name: merchbay_admin_dev
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- APP_ENV=staging
|
||||
- APP_DEBUG=false
|
||||
- APP_URL=https://dev.merchbay.app
|
||||
- DB_CONNECTION=mysql
|
||||
- DB_HOST=your-mysql-host
|
||||
- DB_PORT=3306
|
||||
- DB_DATABASE=merchbay_admin_dev
|
||||
- DB_USERNAME=your-mysql-user
|
||||
- DB_PASSWORD=your-mysql-password
|
||||
volumes:
|
||||
- app_storage_dev:/var/www/html/storage
|
||||
- app_uploads_dev:/var/www/html/public/uploads
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.merchbay-admin-dev.rule=Host(`dev.merchbay.app`)"
|
||||
- "traefik.http.routers.merchbay-admin-dev.entrypoints=websecure"
|
||||
- "traefik.http.routers.merchbay-admin-dev.tls=true"
|
||||
- "traefik.http.routers.merchbay-admin-dev.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.merchbay-admin-dev.loadbalancer.server.port=80"
|
||||
# HTTP to HTTPS redirect
|
||||
- "traefik.http.routers.merchbay-admin-dev-http.rule=Host(`dev.merchbay.app`)"
|
||||
- "traefik.http.routers.merchbay-admin-dev-http.entrypoints=web"
|
||||
- "traefik.http.routers.merchbay-admin-dev-http.middlewares=https-redirect"
|
||||
- "traefik.http.middlewares.https-redirect.redirectscheme.scheme=https"
|
||||
networks:
|
||||
- traefik-public
|
||||
|
||||
volumes:
|
||||
app_storage_dev:
|
||||
driver: local
|
||||
app_uploads_dev:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
traefik-public:
|
||||
external: true
|
||||
51
docker-compose.portainer.yml
Normal file
51
docker-compose.portainer.yml
Normal file
@@ -0,0 +1,51 @@
|
||||
version: '3.8'
|
||||
|
||||
# Production Stack - Portainer Configuration
|
||||
# Deploy this through Portainer UI: Stacks -> Add Stack -> Web Editor
|
||||
# Branch: main - Uses paid SSL certificate
|
||||
|
||||
services:
|
||||
app:
|
||||
image: merchbay_admin:latest
|
||||
container_name: merchbay_admin_prod
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- APP_ENV=production
|
||||
- APP_DEBUG=false
|
||||
- APP_URL=https://merchbay.app
|
||||
- DB_CONNECTION=mysql
|
||||
- DB_HOST=your-mysql-host
|
||||
- DB_PORT=3306
|
||||
- DB_DATABASE=merchbay_admin
|
||||
- DB_USERNAME=your-mysql-user
|
||||
- DB_PASSWORD=your-mysql-password
|
||||
volumes:
|
||||
- app_storage:/var/www/html/storage
|
||||
- app_uploads:/var/www/html/public/uploads
|
||||
# Mount paid SSL certificates
|
||||
- /path/to/ssl/certs:/etc/ssl/certs:ro
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.merchbay-admin.rule=Host(`merchbay.app`)"
|
||||
- "traefik.http.routers.merchbay-admin.entrypoints=websecure"
|
||||
- "traefik.http.routers.merchbay-admin.tls=true"
|
||||
# Use custom TLS configuration (file provider for paid cert)
|
||||
# Ensure Traefik has file provider configured with your paid SSL cert
|
||||
- "traefik.http.services.merchbay-admin.loadbalancer.server.port=80"
|
||||
# HTTP to HTTPS redirect
|
||||
- "traefik.http.routers.merchbay-admin-http.rule=Host(`merchbay.app`)"
|
||||
- "traefik.http.routers.merchbay-admin-http.entrypoints=web"
|
||||
- "traefik.http.routers.merchbay-admin-http.middlewares=https-redirect"
|
||||
- "traefik.http.middlewares.https-redirect.redirectscheme.scheme=https"
|
||||
networks:
|
||||
- traefik-public
|
||||
|
||||
volumes:
|
||||
app_storage:
|
||||
driver: local
|
||||
app_uploads:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
traefik-public:
|
||||
external: true
|
||||
@@ -7,11 +7,10 @@ services:
|
||||
dockerfile: Dockerfile
|
||||
container_name: merchbay_admin_app
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8080:80"
|
||||
environment:
|
||||
- APP_ENV=production
|
||||
- APP_DEBUG=false
|
||||
- APP_ENV=${APP_ENV:-production}
|
||||
- APP_DEBUG=${APP_DEBUG:-false}
|
||||
- APP_URL=${APP_URL:-http://localhost}
|
||||
- DB_CONNECTION=mysql
|
||||
- DB_HOST=${DB_HOST:-localhost}
|
||||
- DB_PORT=${DB_PORT:-3306}
|
||||
@@ -21,4 +20,24 @@ services:
|
||||
volumes:
|
||||
- ./storage:/var/www/html/storage
|
||||
- ./public/uploads:/var/www/html/public/uploads
|
||||
network_mode: bridge
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.merchbay-admin.rule=Host(`${DOMAIN:-merchbay-admin.localhost}`)"
|
||||
- "traefik.http.routers.merchbay-admin.entrypoints=websecure"
|
||||
- "traefik.http.routers.merchbay-admin.tls=true"
|
||||
- "traefik.http.routers.merchbay-admin.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.merchbay-admin.loadbalancer.server.port=80"
|
||||
# HTTP to HTTPS redirect
|
||||
- "traefik.http.routers.merchbay-admin-http.rule=Host(`${DOMAIN:-merchbay-admin.localhost}`)"
|
||||
- "traefik.http.routers.merchbay-admin-http.entrypoints=web"
|
||||
- "traefik.http.routers.merchbay-admin-http.middlewares=https-redirect"
|
||||
- "traefik.http.middlewares.https-redirect.redirectscheme.scheme=https"
|
||||
networks:
|
||||
- traefik-public
|
||||
- default
|
||||
|
||||
networks:
|
||||
traefik-public:
|
||||
external: true
|
||||
default:
|
||||
driver: bridge
|
||||
|
||||
Reference in New Issue
Block a user