diff --git a/README.md b/README.md
index aa8300dc..8bb9ce91 100644
--- a/README.md
+++ b/README.md
@@ -1,76 +1,54 @@
-# Grafana OnCall Incident Response
-Grafana OnCall, cloud version of Grafana OnCall: https://grafana.com/products/cloud/
+
Developer-friendly, incident response management with brilliant Slack integration.
-- Connect monitoring systems
-- Collect and analyze data
-- On-call rotation
-- Automatic escalation
-- Never miss alerts with calls and SMS
-
+
+
+- Collect and analyze alerts from multiple monitoring systems
+- On-call rotations based on schedules
+- Automatic escalations
+- Phone calls, SMS, Slack, Telegram notifications
+
+
+
+
## Getting Started
-OnCall consists of two parts:
-1. OnCall backend
-2. "Grafana OnCall" plugin you need to install in your Grafana
-### How to run OnCall backend
-1. An all-in-one image of OnCall is available on docker hub to run it:
+### Environments:
+
+Production: [PRODUCTION.md](PRODUCTION.md).
+Developer: [DEVELOPER.md](DEVELOPER.md).
+
+### Hobby environment
+
+Download docker-compose.yaml:
```bash
-docker run -it --name oncall-backend -p 8000:8000 grafana/oncall-all-in-one
+curl https://github.com/... -o docker-compose.yaml
```
-2. When the image starts up you will see a message like this:
+Set environment:
```bash
-👋 This script will issue an invite token to securely connect the frontend.
-Maintainers will be happy to help in the slack channel #grafana-oncall: https://slack.grafana.com/
-Your invite token: , use it in the Grafana OnCall plugin.
+export DOMAIN=http://localhost
+export SECRET_KEY=my_random_secret_must_be_more_than_32_characters_long
+export RABBITMQ_PASSWORD=rabbitmq_secret_pw
+export MYSQL_PASSWORD=mysql_secret_pw
+export COMPOSE_PROFILES=with_grafana
+export GRAFANA_USER=admin
+export GRAFANA_PASSWORD=admin
```
-3. If you started your container detached with -d check the log:
+Launch services:
```bash
-docker logs oncall-backend
+docker-compose -f docker-compose.yml up --build -d
```
-### How to install "Grafana OnCall" Plugin and connect with a backend
-1. Open Grafana in your browser and login as an Admin
-2. Navigate to Configuration → Plugins
-3. Type Grafana OnCall into the "Search Grafana plugins" field
-4. Select the Grafana OnCall plugin and press the "Install" button
-5. On the Grafana OnCall Plugin page Enable the plugin and go to the Configuration tab you should see a status field with the message
+Issue invite token and get further instructions:
+```bash
+docker-compose -f docker-compose.yml run engine python manage.py issue_invite_for_the_frontend --override
```
-OnCall has not been setup, configure & initialize below.
-```
-6. Fill in configuration fields using the token you got from the backend earlier, then press "Install Configuration"
-```
-OnCall API URL: (The URL & port used to access OnCall)
-http://host.docker.internal:8000
-
-OnCall Invitation Token (Single use token to connect Grafana instance):
-Invitation token from docker startup
-
-Grafana URL (URL OnCall will use to talk to this Grafana instance):
-http://localhost:3000 (or http://host.docker.internal:3000 if your grafana is running in Docker locally)
-```
-
-## Getting Help
-- `#grafana-oncall` channel at https://slack.grafana.com/
-- Grafana Labs community forum for OnCall: https://community.grafana.com
-- File an [issue](https://github.com/grafana/oncall/issues) for bugs, issues and feature suggestions.
-
-## Production Setup
-
-Looking for the production instructions? We're going to release them soon. Please join our Slack channel to be the first to know about them.
## Further Reading
- *Documentation* - [Grafana OnCall](https://grafana.com/docs/grafana-cloud/oncall/)
- *Blog Post* - [Announcing Grafana OnCall, the easiest way to do on-call management](https://grafana.com/blog/2021/11/09/announcing-grafana-oncall/)
- *Presentation* - [Deep dive into the Grafana, Prometheus, and Alertmanager stack for alerting and on-call management](https://grafana.com/go/observabilitycon/2021/alerting/?pg=blog)
-
-## FAQ
-
-- How do I generate a new invitation token to connect plugin with a backend?
-```bash
-docker exec oncall-backend python manage.py issue_invite_for_the_frontend --override
-```
diff --git a/deploy/docker-compose/README.md b/deploy/docker-compose/README.md
new file mode 100644
index 00000000..e69de29b
diff --git a/deploy/docker-compose/docker-compose.yml b/deploy/docker-compose/docker-compose.yml
new file mode 100644
index 00000000..a07bb72e
--- /dev/null
+++ b/deploy/docker-compose/docker-compose.yml
@@ -0,0 +1,169 @@
+services:
+ engine:
+ # TODO: change to the public image once it's public
+ # image: ...
+ build: ../../engine
+ ports:
+ - 8080:8080
+ command: >
+ sh -c "uwsgi --ini uwsgi.ini"
+ environment:
+ BASE_URL: https://$DOMAIN
+ SECRET_KEY: $SECRET_KEY
+ RABBITMQ_USERNAME: "rabbitmq"
+ RABBITMQ_PASSWORD: $RABBITMQ_PASSWORD
+ RABBITMQ_HOST: "rabbitmq"
+ RABBITMQ_PORT: "5672"
+ RABBITMQ_DEFAULT_VHOST: "/"
+ MYSQL_PASSWORD: $MYSQL_PASSWORD
+ MYSQL_DB_NAME: oncall_hobby
+ MYSQL_USER: ${MYSQL_USER:-root}
+ MYSQL_HOST: ${MYSQL_HOST:-mysql}
+ MYSQL_PORT: 3306
+ REDIS_URI: redis://redis:6379/0
+ DJANGO_SETTINGS_MODULE: settings.hobby
+ depends_on:
+ mysql:
+ condition: service_healthy
+ oncall_db_migration:
+ condition: service_completed_successfully
+ rabbitmq:
+ condition: service_started
+ redis:
+ condition: service_started
+
+ celery:
+ # TODO: change to the public image once it's public
+ build: ../../engine
+ command: sh -c "./celery_with_exporter.sh"
+ environment:
+ BASE_URL: https://$DOMAIN
+ SECRET_KEY: $SECRET_KEY
+ RABBITMQ_USERNAME: "rabbitmq"
+ RABBITMQ_PASSWORD: $RABBITMQ_PASSWORD
+ RABBITMQ_HOST: "rabbitmq"
+ RABBITMQ_PORT: "5672"
+ RABBITMQ_DEFAULT_VHOST: "/"
+ MYSQL_PASSWORD: $MYSQL_PASSWORD
+ MYSQL_DB_NAME: oncall_hobby
+ MYSQL_USER: ${MYSQL_USER:-root}
+ MYSQL_HOST: ${MYSQL_HOST:-mysql}
+ MYSQL_PORT: 3306
+ REDIS_URI: redis://redis:6379/0
+ DJANGO_SETTINGS_MODULE: settings.hobby
+ CELERY_WORKER_QUEUE: "celery"
+ CELERY_WORKER_CONCURRENCY: "1"
+ CELERY_WORKER_MAX_TASKS_PER_CHILD: "100"
+ CELERY_WORKER_SHUTDOWN_INTERVAL: "65m"
+ CELERY_WORKER_BEAT_ENABLED: "True"
+ depends_on:
+ mysql:
+ condition: service_healthy
+ oncall_db_migration:
+ condition: service_completed_successfully
+ rabbitmq:
+ condition: service_started
+ redis:
+ condition: service_started
+
+ oncall_db_migration:
+ build: ../../engine
+ command: python manage.py migrate --noinput
+ environment:
+ BASE_URL: https://$DOMAIN
+ SECRET_KEY: $SECRET_KEY
+ RABBITMQ_USERNAME: "rabbitmq"
+ RABBITMQ_PASSWORD: $RABBITMQ_PASSWORD
+ RABBITMQ_HOST: "rabbitmq"
+ RABBITMQ_PORT: "5672"
+ RABBITMQ_DEFAULT_VHOST: "/"
+ MYSQL_PASSWORD: $MYSQL_PASSWORD
+ MYSQL_DB_NAME: oncall_hobby
+ MYSQL_USER: ${MYSQL_USER:-root}
+ MYSQL_HOST: ${MYSQL_HOST:-mysql}
+ MYSQL_PORT: 3306
+ REDIS_URI: redis://redis:6379/0
+ DJANGO_SETTINGS_MODULE: settings.hobby
+ depends_on:
+ mysql:
+ condition: service_healthy
+
+ mysql:
+ image: mysql:5.7
+ platform: linux/x86_64
+ mem_limit: 500m
+ cpus: 0.5
+ command: --default-authentication-plugin=mysql_native_password
+ restart: always
+ ports:
+ - 3306:3306
+ volumes:
+ - dbdata:/var/lib/mysql
+ environment:
+ MYSQL_ROOT_PASSWORD: $MYSQL_PASSWORD
+ MYSQL_DATABASE: oncall_hobby
+ healthcheck:
+ test: "mysql -uroot -p$MYSQL_PASSWORD oncall_hobby -e 'select 1'"
+ timeout: 20s
+ retries: 10
+
+ redis:
+ image: redis
+ mem_limit: 100m
+ cpus: 0.1
+ restart: always
+ ports:
+ - 6379:6379
+
+ rabbitmq:
+ image: "rabbitmq:3.7.15-management"
+ hostname: rabbitmq
+ mem_limit: 1000m
+ cpus: 0.5
+ volumes:
+ - rabbitmqdata:/var/lib/rabbitmq
+ environment:
+ RABBITMQ_DEFAULT_USER: "rabbitmq"
+ RABBITMQ_DEFAULT_PASS: $RABBITMQ_PASSWORD
+ RABBITMQ_DEFAULT_VHOST: "/"
+
+ mysql_to_create_grafana_db:
+ image: mysql:5.7
+ platform: linux/x86_64
+ command: bash -c "mysql -h ${MYSQL_HOST:-mysql} -uroot -p${MYSQL_PASSWORD:?err} -e 'CREATE DATABASE IF NOT EXISTS grafana CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'"
+ depends_on:
+ mysql:
+ condition: service_healthy
+ profiles:
+ - with_grafana
+
+ grafana:
+ image: "grafana/grafana:8.3.2"
+ mem_limit: 500m
+ ports:
+ - 3000:3000
+ cpus: 0.5
+ environment:
+ GF_DATABASE_TYPE: mysql
+ GF_DATABASE_HOST: ${MYSQL_HOST:-mysql}
+ GF_DATABASE_USER: ${MYSQL_USER:-root}
+ GF_DATABASE_PASSWORD: ${MYSQL_PASSWORD:?err}
+ GF_SECURITY_ADMIN_USER: ${GRAFANA_USER:-admin}
+ GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD:?err}
+ GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS: grafana-oncall-app
+ GF_INSTALL_PLUGINS: grafana-oncall-app
+ volumes:
+ - ../../grafana-plugin:/var/lib/grafana/plugins/grafana-plugin
+ depends_on:
+ mysql_to_create_grafana_db:
+ condition: service_completed_successfully
+ mysql:
+ condition: service_healthy
+ profiles:
+ - with_grafana
+
+volumes:
+ dbdata:
+ rabbitmqdata:
+ caddy_data:
+ caddy_config:
diff --git a/docs/img/GH_discussions.png b/docs/img/GH_discussions.png
new file mode 100644
index 00000000..d3a1798a
Binary files /dev/null and b/docs/img/GH_discussions.png differ
diff --git a/docs/img/community_call.png b/docs/img/community_call.png
new file mode 100644
index 00000000..22692fad
Binary files /dev/null and b/docs/img/community_call.png differ
diff --git a/docs/img/logo.png b/docs/img/logo.png
new file mode 100644
index 00000000..ec11fc3a
Binary files /dev/null and b/docs/img/logo.png differ
diff --git a/docs/img/slack.png b/docs/img/slack.png
new file mode 100644
index 00000000..d5ec5e6d
Binary files /dev/null and b/docs/img/slack.png differ
diff --git a/docs/sources/integrations/webhooks/_index.md b/docs/sources/integrations/webhooks/_index.md
index 026458a8..c5a92ffe 100644
--- a/docs/sources/integrations/webhooks/_index.md
+++ b/docs/sources/integrations/webhooks/_index.md
@@ -9,4 +9,4 @@ You can use webhooks to send alert group notifications, and also to receive aler
Follow these links to learn more about using webhooks for OnCall alert notifications:
-{{< section >}}
\ No newline at end of file
+{{< section >}}
diff --git a/engine/settings/hobby.py b/engine/settings/hobby.py
new file mode 100644
index 00000000..4b2a4e8f
--- /dev/null
+++ b/engine/settings/hobby.py
@@ -0,0 +1,57 @@
+# flake8: noqa: F405
+
+from random import randrange
+
+# Workaround to use pymysql instead of mysqlclient
+import pymysql
+
+from .prod_without_db import * # noqa
+
+pymysql.install_as_MySQLdb()
+
+DATABASES = {
+ "default": {
+ "ENGINE": "django.db.backends.mysql",
+ "NAME": os.environ.get("MYSQL_DB_NAME"),
+ "USER": os.environ.get("MYSQL_USER"),
+ "PASSWORD": os.environ["MYSQL_PASSWORD"],
+ "HOST": os.environ.get("MYSQL_HOST"),
+ "PORT": os.environ.get("MYSQL_PORT"),
+ "OPTIONS": {
+ "charset": "utf8mb4",
+ "connect_timeout": 1,
+ },
+ },
+}
+
+RABBITMQ_USERNAME = os.environ.get("RABBITMQ_USERNAME")
+RABBITMQ_PASSWORD = os.environ.get("RABBITMQ_PASSWORD")
+RABBITMQ_HOST = os.environ.get("RABBITMQ_HOST")
+RABBITMQ_PORT = os.environ.get("RABBITMQ_PORT")
+
+CELERY_BROKER_URL = f"amqp://{RABBITMQ_USERNAME}:{RABBITMQ_PASSWORD}@{RABBITMQ_HOST}:{RABBITMQ_PORT}"
+
+MIRAGE_SECRET_KEY = SECRET_KEY
+MIRAGE_CIPHER_IV = "1234567890abcdef" # use default
+
+APPEND_SLASH = False
+SECURE_SSL_REDIRECT = False
+
+# TODO: OSS: Add these setting to oss settings file. Add Version there too.
+OSS_INSTALLATION_FEATURES_ENABLED = True
+
+INSTALLED_APPS += ["apps.oss_installation"] # noqa
+
+CELERY_BEAT_SCHEDULE["send_usage_stats"] = { # noqa
+ "task": "apps.oss_installation.tasks.send_usage_stats_report",
+ "schedule": crontab(hour=0, minute=randrange(0, 59)), # Send stats report at a random minute past midnight # noqa
+ "args": (),
+} # noqa
+
+CELERY_BEAT_SCHEDULE["send_cloud_heartbeat"] = { # noqa
+ "task": "apps.oss_installation.tasks.send_cloud_heartbeat",
+ "schedule": crontab(minute="*/3"), # noqa
+ "args": (),
+} # noqa
+
+SEND_ANONYMOUS_USAGE_STATS = True