| 1 | #!/bin/bash |
| 2 | |
| 3 | # WordPress Multi-Site Manager |
| 4 | # Създава изолирани WordPress инсталации с Docker |
| 5 | |
| 6 | set -e |
| 7 | |
| 8 | SITES_DIR="./sites" |
| 9 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| 10 | |
| 11 | # Цветове заOutput |
| 12 | GREEN='\033[0;32m' |
| 13 | BLUE='\033[0;34m' |
| 14 | RED='\033[0;31m' |
| 15 | NC='\033[0m' |
| 16 | |
| 17 | # Генериране на силна парола |
| 18 | generate_password() { |
| 19 | openssl rand -base64 32 | tr -d "=+/" | cut -c1-25 |
| 20 | } |
| 21 | |
| 22 | # Намиране на свободен порт |
| 23 | find_free_port() { |
| 24 | local start_port=8080 |
| 25 | while netstat -tuln 2>/dev/null | grep -q ":$start_port "; do |
| 26 | start_port=$((start_port + 1)) |
| 27 | done |
| 28 | echo $start_port |
| 29 | } |
| 30 | |
| 31 | # Създаване на нов сайт |
| 32 | create_site() { |
| 33 | local site_name=$1 |
| 34 | |
| 35 | if [ -z "$site_name" ]; then |
| 36 | echo -e "${RED}Грешка: Въведи име на сайта${NC}" |
| 37 | echo "Примерно: ./wp-manager.sh create mysite" |
| 38 | exit 1 |
| 39 | fi |
| 40 | |
| 41 | local site_dir="$SITES_DIR/$site_name" |
| 42 | |
| 43 | if [ -d "$site_dir" ]; then |
| 44 | echo -e "${RED}Грешка: Сайт '$site_name' вече съществува${NC}" |
| 45 | exit 1 |
| 46 | fi |
| 47 | |
| 48 | echo -e "${BLUE}Създавам WordPress сайт: $site_name${NC}" |
| 49 | |
| 50 | # Създаване на директории |
| 51 | mkdir -p "$site_dir"/{wordpress,mysql} |
| 52 | |
| 53 | # Генериране на пароли |
| 54 | DB_PASSWORD=$(generate_password) |
| 55 | WP_ADMIN_PASSWORD=$(generate_password) |
| 56 | |
| 57 | # Намиране на свободен порт |
| 58 | HTTP_PORT=$(find_free_port) |
| 59 | |
| 60 | # Създаване на .env файл |
| 61 | cat > "$site_dir/.env" << EOF |
| 62 | # WordPress Site: $site_name |
| 63 | # Създаден на: $(date) |
| 64 | |
| 65 | # MySQL настройки |
| 66 | MYSQL_ROOT_PASSWORD=$(generate_password) |
| 67 | MYSQL_DATABASE=wordpress |
| 68 | MYSQL_USER=wpuser |
| 69 | MYSQL_PASSWORD=$DB_PASSWORD |
| 70 | |
| 71 | # WordPress настройки |
| 72 | WORDPRESS_DB_HOST=db:3306 |
| 73 | WORDPRESS_DB_NAME=wordpress |
| 74 | WORDPRESS_DB_USER=wpuser |
| 75 | WORDPRESS_DB_PASSWORD=$DB_PASSWORD |
| 76 | |
| 77 | # Admin достъп |
| 78 | WP_ADMIN_USER=admin |
| 79 | WP_ADMIN_PASSWORD=$WP_ADMIN_PASSWORD |
| 80 | WP_ADMIN_EMAIL=admin@$site_name.local |
| 81 | |
| 82 | # Портове |
| 83 | HTTP_PORT=$HTTP_PORT |
| 84 | EOF |
| 85 | |
| 86 | # Създаване на docker-compose.yml |
| 87 | cat > "$site_dir/docker-compose.yml" << 'EOF' |
| 88 | version: '3.8' |
| 89 | |
| 90 | services: |
| 91 | db: |
| 92 | image: mysql:8.0 |
| 93 | container_name: ${COMPOSE_PROJECT_NAME}_db |
| 94 | restart: unless-stopped |
| 95 | volumes: |
| 96 | - ./mysql:/var/lib/mysql |
| 97 | environment: |
| 98 | MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} |
| 99 | MYSQL_DATABASE: ${MYSQL_DATABASE} |
| 100 | MYSQL_USER: ${MYSQL_USER} |
| 101 | MYSQL_PASSWORD: ${MYSQL_PASSWORD} |
| 102 | networks: |
| 103 | - wp_network |
| 104 | |
| 105 | wordpress: |
| 106 | image: wordpress:latest |
| 107 | container_name: ${COMPOSE_PROJECT_NAME}_wp |
| 108 | restart: unless-stopped |
| 109 | depends_on: |
| 110 | - db |
| 111 | ports: |
| 112 | - "${HTTP_PORT}:80" |
| 113 | volumes: |
| 114 | - ./wordpress:/var/www/html |
| 115 | - ./php.ini:/usr/local/etc/php/conf.d/custom.ini |
| 116 | environment: |
| 117 | WORDPRESS_DB_HOST: ${WORDPRESS_DB_HOST} |
| 118 | WORDPRESS_DB_NAME: ${WORDPRESS_DB_NAME} |
| 119 | WORDPRESS_DB_USER: ${WORDPRESS_DB_USER} |
| 120 | WORDPRESS_DB_PASSWORD: ${WORDPRESS_DB_PASSWORD} |
| 121 | networks: |
| 122 | - wp_network |
| 123 | |
| 124 | networks: |
| 125 | wp_network: |
| 126 | driver: bridge |
| 127 | EOF |
| 128 | |
| 129 | # Създаване на php.ini |
| 130 | cat > "$site_dir/php.ini" << EOF |
| 131 | ; Custom PHP settings |
| 132 | upload_max_filesize = 256M |
| 133 | post_max_size = 256M |
| 134 | memory_limit = 512M |
| 135 | max_execution_time = 300 |
| 136 | max_input_time = 300 |
| 137 | max_input_vars = 3000 |
| 138 | |
| 139 | ; Error logging |
| 140 | display_errors = Off |
| 141 | log_errors = On |
| 142 | error_log = /var/log/php_errors.log |
| 143 | |
| 144 | ; Session |
| 145 | session.gc_maxlifetime = 1440 |
| 146 | session.cookie_httponly = On |
| 147 | |
| 148 | ; Security |
| 149 | expose_php = Off |
| 150 | allow_url_fopen = On |
| 151 | EOF |
| 152 | |
| 153 | # Стартиране на контейнерите |
| 154 | cd "$site_dir" |
| 155 | COMPOSE_PROJECT_NAME="${site_name}" docker-compose up -d |
| 156 | |
| 157 | echo -e "${GREEN}✓ Сайт '$site_name' създаден успешно!${NC}" |
| 158 | echo "" |
| 159 | echo -e "${BLUE}Достъп до сайта:${NC}" |
| 160 | echo " URL: http://localhost:$HTTP_PORT" |
| 161 | echo " Admin: http://localhost:$HTTP_PORT/wp-admin" |
| 162 | echo "" |
| 163 | echo -e "${BLUE}Admin креденшъли:${NC}" |
| 164 | echo " Потребител: admin" |
| 165 | echo " Парола: $WP_ADMIN_PASSWORD" |
| 166 | echo "" |
| 167 | echo -e "${BLUE}Файлове:${NC}" |
| 168 | echo " Локация: $site_dir" |
| 169 | echo " .env файл: $site_dir/.env" |
| 170 | echo "" |
| 171 | echo -e "${GREEN}Изчакай 30-60 секунди за инициализация на базата${NC}" |
| 172 | } |
| 173 | |
| 174 | # Спиране на сайт |
| 175 | stop_site() { |
| 176 | local site_name=$1 |
| 177 | local site_dir="$SITES_DIR/$site_name" |
| 178 | |
| 179 | if [ ! -d "$site_dir" ]; then |
| 180 | echo -e "${RED}Грешка: Сайт '$site_name' не съществува${NC}" |
| 181 | exit 1 |
| 182 | fi |
| 183 | |
| 184 | cd "$site_dir" |
| 185 | COMPOSE_PROJECT_NAME="${site_name}" docker-compose stop |
| 186 | echo -e "${GREEN}✓ Сайт '$site_name' спрян${NC}" |
| 187 | } |
| 188 | |
| 189 | # Стартиране на сайт |
| 190 | start_site() { |
| 191 | local site_name=$1 |
| 192 | local site_dir="$SITES_DIR/$site_name" |
| 193 | |
| 194 | if [ ! -d "$site_dir" ]; then |
| 195 | echo -e "${RED}Грешка: Сайт '$site_name' не съществува${NC}" |
| 196 | exit 1 |
| 197 | fi |
| 198 | |
| 199 | cd "$site_dir" |
| 200 | COMPOSE_PROJECT_NAME="${site_name}" docker-compose start |
| 201 | |
| 202 | source .env |
| 203 | echo -e "${GREEN}✓ Сайт '$site_name' стартиран${NC}" |
| 204 | echo " URL: http://localhost:$HTTP_PORT" |
| 205 | } |
| 206 | |
| 207 | # Изтриване на сайт |
| 208 | delete_site() { |
| 209 | local site_name=$1 |
| 210 | local site_dir="$SITES_DIR/$site_name" |
| 211 | |
| 212 | if [ ! -d "$site_dir" ]; then |
| 213 | echo -e "${RED}Грешка: Сайт '$site_name' не съществува${NC}" |
| 214 | exit 1 |
| 215 | fi |
| 216 | |
| 217 | read -p "Сигурен ли си, че искаш да изтриеш '$site_name'? (yes/no): " confirm |
| 218 | if [ "$confirm" != "yes" ]; then |
| 219 | echo "Отказано" |
| 220 | exit 0 |
| 221 | fi |
| 222 | |
| 223 | cd "$site_dir" |
| 224 | COMPOSE_PROJECT_NAME="${site_name}" docker-compose down -v |
| 225 | cd .. |
| 226 | rm -rf "$site_dir" |
| 227 | |
| 228 | echo -e "${GREEN}✓ Сайт '$site_name' изтрит${NC}" |
| 229 | } |
| 230 | |
| 231 | # Списък със сайтове |
| 232 | list_sites() { |
| 233 | if [ ! -d "$SITES_DIR" ] || [ -z "$(ls -A $SITES_DIR 2>/dev/null)" ]; then |
| 234 | echo "Няма създадени сайтове" |
| 235 | exit 0 |
| 236 | fi |
| 237 | |
| 238 | echo -e "${BLUE}Налични WordPress сайтове:${NC}" |
| 239 | echo "" |
| 240 | |
| 241 | for site_path in "$SITES_DIR"/*/ ; do |
| 242 | if [ -d "$site_path" ]; then |
| 243 | site_name=$(basename "$site_path") |
| 244 | env_file="$site_path.env" |
| 245 | |
| 246 | if [ -f "$env_file" ]; then |
| 247 | # Вземаме порта от .env |
| 248 | HTTP_PORT=$(grep "^HTTP_PORT=" "$env_file" | cut -d'=' -f2) |
| 249 | |
| 250 | # Проверяваме дали контейнерът работи (без wp_ префикс) |
| 251 | container_id=$(docker ps -q -f "name=${site_name}_wp" 2>/dev/null) |
| 252 | |
| 253 | if [ -n "$container_id" ]; then |
| 254 | echo -e " ${GREEN}●${NC} $site_name - http://localhost:$HTTP_PORT" |
| 255 | else |
| 256 | echo -e " ${RED}○${NC} $site_name - http://localhost:$HTTP_PORT (спрян)" |
| 257 | fi |
| 258 | fi |
| 259 | fi |
| 260 | done |
| 261 | } |
| 262 | |
| 263 | # Показване на информация за сайт |
| 264 | info_site() { |
| 265 | local site_name=$1 |
| 266 | local site_dir="$SITES_DIR/$site_name" |
| 267 | |
| 268 | if [ ! -d "$site_dir" ]; then |
| 269 | echo -e "${RED}Грешка: Сайт '$site_name' не съществува${NC}" |
| 270 | exit 1 |
| 271 | fi |
| 272 | |
| 273 | source "$site_dir/.env" |
| 274 | |
| 275 | echo -e "${BLUE}Информация за сайт: $site_name${NC}" |
| 276 | echo "" |
| 277 | echo "URL: http://localhost:$HTTP_PORT" |
| 278 | echo "Admin: http://localhost:$HTTP_PORT/wp-admin" |
| 279 | echo "" |
| 280 | echo "WordPress Admin:" |
| 281 | echo " Потребител: $WP_ADMIN_USER" |
| 282 | echo " Парола: $WP_ADMIN_PASSWORD" |
| 283 | echo "" |
| 284 | echo "MySQL:" |
| 285 | echo " База: $MYSQL_DATABASE" |
| 286 | echo " Потребител: $MYSQL_USER" |
| 287 | echo " Парола: $MYSQL_PASSWORD" |
| 288 | echo "" |
| 289 | echo "Файлове: $site_dir" |
| 290 | } |
| 291 | |
| 292 | # Помощна информация |
| 293 | show_help() { |
| 294 | cat << EOF |
| 295 | WordPress Multi-Site Manager |
| 296 | |
| 297 | Използване: |
| 298 | ./wp-manager.sh <команда> [опции] |
| 299 | |
| 300 | Команди: |
| 301 | create <име> Създаване на нов WordPress сайт |
| 302 | start <име> Стартиране на съществуващ сайт |
| 303 | stop <име> Спиране на работещ сайт |
| 304 | delete <име> Изтриване на сайт (с потвърждение) |
| 305 | list Показване на всички сайтове |
| 306 | info <име> Информация и креденшъли за сайт |
| 307 | help Показване на тази помощ |
| 308 | |
| 309 | Примери: |
| 310 | ./wp-manager.sh create mysite |
| 311 | ./wp-manager.sh list |
| 312 | ./wp-manager.sh info mysite |
| 313 | ./wp-manager.sh stop mysite |
| 314 | ./wp-manager.sh delete mysite |
| 315 | |
| 316 | EOF |
| 317 | } |
| 318 | |
| 319 | # Главна логика |
| 320 | main() { |
| 321 | mkdir -p "$SITES_DIR" |
| 322 | |
| 323 | case "${1:-}" in |
| 324 | create) |
| 325 | create_site "$2" |
| 326 | ;; |
| 327 | start) |
| 328 | start_site "$2" |
| 329 | ;; |
| 330 | stop) |
| 331 | stop_site "$2" |
| 332 | ;; |
| 333 | delete) |
| 334 | delete_site "$2" |
| 335 | ;; |
| 336 | list) |
| 337 | list_sites |
| 338 | ;; |
| 339 | info) |
| 340 | info_site "$2" |
| 341 | ;; |
| 342 | help|--help|-h) |
| 343 | show_help |
| 344 | ;; |
| 345 | *) |
| 346 | show_help |
| 347 | exit 1 |
| 348 | ;; |
| 349 | esac |
| 350 | } |
| 351 | |
| 352 | main "$@" |
WordPress Multi-Site Manager - Инструкции
Система за лесно създаване и управление на множество WordPress сайтове с Docker.
🚀 Първоначална инсталация
# 1. Създай файла
nano wp-manager.sh
# 2. Копирай скрипта вътре
# 3. Направи го изпълним
chmod +x wp-manager.sh
# 4. Добави Docker права (само първи път)
sudo usermod -aG docker $USER
newgrp docker
📋 Основни команди
Създаване на нов сайт
./wp-manager.sh create mysite
- Автоматично генерира пароли
- Намира свободен порт
- Стартира MySQL + WordPress
- Готово за ползване след 30-60 секунди
Списък с всички сайтове
./wp-manager.sh list
- Показва активни (●) и спрени (○) сайтове
- Показва портовете на всеки сайт
Информация за сайт (ВАЖНО!)
./wp-manager.sh info mysite
ТУК СА ВСИЧКИ ПАРОЛИ И ДАННИ:
- URL на сайта
- Admin потребител и парола
- MySQL креденшъли
- Локация на файловете
Спиране на сайт
./wp-manager.sh stop mysite
Спира контейнерите, но запазва данните
Стартиране на сайт
./wp-manager.sh start mysite
Стартира спрян сайт
Изтриване на сайт
./wp-manager.sh delete mysite
ВНИМАНИЕ: Изтрива всичко - файлове, база данни, всичко!
Ще поиска потвърждение (трябва да напишеш yes)
Помощ
./wp-manager.sh help
🌐 Как да отворя сайта
Локално (на същата машина):
http://localhost:8080
От друг компютър в мрежата:
http://10.20.20.79:8080
(Замени с твоя IP и порт)
WordPress Admin:
http://10.20.20.79:8080/wp-admin
Портът зависи от сайта! Виж го с:
./wp-manager.sh list # или
./wp-manager.sh info mysite
🔥 Отваряне на портове във firewall
Ако не можеш да достъпиш сайта отвън:
# За UFW (Ubuntu/Debian)
sudo ufw allow 8080/tcp
sudo ufw allow 8081/tcp
sudo ufw allow 8082/tcp
# За Firewalld (CentOS/RHEL)
sudo firewall-cmd --add-port=8080-8090/tcp --permanent
sudo firewall-cmd --reload
# Провери статуса
sudo ufw status
# или
sudo firewall-cmd --list-ports
📁 Структура на файловете
.
├── wp-manager.sh # Главният скрипт
├── README.md # Този файл
└── sites/ # Всички сайтове са тук
├── mysite/
│ ├── docker-compose.yml
│ ├── .env # ПАРОЛИ И НАСТРОЙКИ
│ ├── php.ini # PHP конфигурация
│ ├── wordpress/ # WordPress файлове
│ └── mysql/ # MySQL база данни
├── site2/
└── site3/
🔧 Работа с файловете на сайта
WordPress файлове:
cd sites/mysite/wordpress
Тук са всички WordPress файлове (themes, plugins, uploads)
Backup на сайт:
# Архивирай целия сайт
tar -czf mysite-backup.tar.gz sites/mysite/
# Възстанови
tar -xzf mysite-backup.tar.gz
./wp-manager.sh start mysite
Преглед на логове:
cd sites/mysite
docker-compose logs -f wordpress # WordPress логове
docker-compose logs -f db # MySQL логове
💡 Примерни сценарии
Създаване на 3 тестови сайта наведнъж:
./wp-manager.sh create test1
./wp-manager.sh create test2
./wp-manager.sh create test3
./wp-manager.sh list
Временно спиране на сайт:
./wp-manager.sh stop test2
# ... работиш по друго ...
./wp-manager.sh start test2
Виждане на всички пароли за сайт:
./wp-manager.sh info test1
# или директно
cat sites/test1/.env
Production deployment:
# Създай сайта
./wp-manager.sh create production
# Вземи данните
./wp-manager.sh info production
# Отвори порта
sudo ufw allow 8080/tcp
# Готово! http://your-server-ip:8080
⚙️ PHP настройки
Всеки сайт има php.ini с оптимизирани настройки:
- Upload limit: 256MB
- Memory limit: 512MB
- Execution time: 300 секунди
- Max vars: 3000
Можеш да ги промениш в sites/mysite/php.ini и рестартираш:
./wp-manager.sh stop mysite
./wp-manager.sh start mysite
🔒 Сигурност
- Паролите се генерират автоматично и са 25 символа
- MySQL не е достъпна отвън (само за WordPress)
- Всеки сайт е изолиран в собствена Docker мрежа
- .env файловете съдържат чувствителни данни - не ги качвай в Git!
За production:
# Променете admin паролата след първо влизане!
# Инсталирайте SSL сертификат
# Добавете .htaccess за сигурност
🐛 Проблеми и решения
Сайтът не се отваря след създаване
# Изчакай 60 секунди за MySQL инициализация
# Провери дали контейнерите работят:
cd sites/mysite
docker-compose ps
Порт е зает
# Скриптът автоматично намира свободен порт
# Ако има проблем, спри стария сайт:
./wp-manager.sh stop oldsite
Permission denied
sudo chown -R $USER:$USER sites/
Docker not running
sudo systemctl start docker
sudo systemctl enable docker
База данни не се свързва
cd sites/mysite
docker-compose down
docker-compose up -d
# Изчакай 30 секунди
📊 Ресурси
- Документация: WordPress Docker
- Docker Compose: docs.docker.com
- PHP настройки: php.net
🎯 Бързи команди (за копиране)
# Нов сайт
./wp-manager.sh create mysite
# Виж пароли
./wp-manager.sh info mysite
# Списък
./wp-manager.sh list
# Отвори порт
sudo ufw allow 8080/tcp
# Логове
cd sites/mysite && docker-compose logs -f
# Backup
tar -czf backup.tar.gz sites/mysite/
📝 Бележки
- Всеки сайт използва около 500MB-1GB RAM
- MySQL данните са в
sites/mysite/mysql/ - WordPress файловете са в
sites/mysite/wordpress/ - Сайтовете са production-ready - можеш да ги използваш направо
- Не е нужен Nginx/Apache - Docker се грижи за всичко
Направено с ❤️ за лесна WordPress разработка
Последна актуализация: 2026-01-14