├── .gitignore ├── VERSION ├── pre-deploy ├── plugin.toml ├── pre-delete ├── pre-build-buildpack ├── install ├── LICENSE ├── functions ├── README.md └── commands /.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.4.0.1 2 | -------------------------------------------------------------------------------- /pre-deploy: -------------------------------------------------------------------------------- 1 | pre-build-buildpack -------------------------------------------------------------------------------- /plugin.toml: -------------------------------------------------------------------------------- 1 | [plugin] 2 | description = "PostgreSQL single container service" 3 | version = "0.4.0.1" 4 | [plugin.config] 5 | -------------------------------------------------------------------------------- /pre-delete: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x 3 | 4 | [[ -n $1 ]] && dokku psql:delete "$1" || exit 0 5 | -------------------------------------------------------------------------------- /pre-build-buildpack: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x 3 | source "$(dirname $0)/functions" 4 | 5 | export_psql_root 6 | [[ ! -f "$PSQL_ROOT/db_$1" ]] && exit 0 7 | load_container_id 8 | [[ -z "$PSQL_ID" ]] && dokku psql:start 9 | exit 0 10 | -------------------------------------------------------------------------------- /install: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x 3 | source "$(dirname $0)/functions" 4 | 5 | export_psql_root 6 | [[ -d "$PSQL_ROOT" ]] && exit 0 7 | mkdir -p "$PSQL_ROOT/data" 8 | chown dokku:dokku -R "$PSQL_ROOT" &> /dev/null || true 9 | PASSWORD=$(openssl rand -hex 32) 10 | export_psql_image 11 | docker run $(bind_ip_options) -v "$PSQL_ROOT/data":/var/lib/postgresql/data -d -e POSTGRES_PASSWORD=$PASSWORD --name "$PSQL_CONTAINER_NAME" "$PSQL_IMAGE" 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2010-2014 Olivier Hardy 4 | Copyright (c) 2015 Loïc Guitaut 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /functions: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x 3 | 4 | APP="$2" 5 | PSQL_CONTAINER_NAME="psql-single-container" 6 | PSQL_PORT=5432 7 | PSQL_HOST=postgres 8 | 9 | check_already_exists() { 10 | [[ -f "$PSQL_ROOT/db_$APP" ]] && echo "Database for $APP already exists" && exit 1 11 | } 12 | 13 | check_exists() { 14 | [[ ! -f "$PSQL_ROOT/db_$APP" ]] && echo "No database configured for $APP" && exit 1 15 | } 16 | 17 | check_app() { 18 | [[ -z "$APP" ]] && echo "Please specify an app to run the command on" && exit 1 19 | } 20 | 21 | check_container() { 22 | [[ -z "$PSQL_ID" ]] && echo "Postgresql container not started. Start with dokku psql:start" && exit 1 23 | } 24 | 25 | load_container_id() { 26 | PSQL_ID=$(docker ps -f status=running | grep "$PSQL_CONTAINER_NAME" | awk '{print $1}') || true 27 | } 28 | 29 | db_url() { 30 | local APP="$1" 31 | local DATABASE=$(cat "$PSQL_ROOT/db_$APP") 32 | local PASSWORD=$(cat "$PSQL_ROOT/pass_$APP") 33 | echo "postgresql://${DATABASE}:${PASSWORD}@${PSQL_HOST}:${PSQL_PORT}/${DATABASE}" 34 | } 35 | 36 | database_name() { 37 | echo "db_$1" | tr .- _ 38 | } 39 | 40 | env_for() { 41 | local APP="$1" 42 | if [[ -f "$PSQL_ROOT/db_$APP" ]] && [[ -f "$PSQL_ROOT/pass_$APP" ]]; then 43 | local URL=$(db_url "$APP") 44 | local DATABASE=$(cat "$PSQL_ROOT/db_$APP") 45 | local PASSWORD=$(cat "$PSQL_ROOT/pass_$APP") 46 | echo "DATABASE_URL=$URL POSTGRESQL_URL=$URL DB_TYPE=postgresql DB_HOST=$PSQL_HOST DB_PORT=$PSQL_PORT DB_NAME=$DATABASE DB_USER=$DATABASE DB_PASS=$PASSWORD" 47 | fi 48 | } 49 | 50 | set_link_and_env_for() { 51 | local SOURCE="$1" 52 | local TARGET=${2:-$SOURCE} 53 | dokku docker-options:add "$TARGET" build,deploy,run "--link $PSQL_CONTAINER_NAME:$PSQL_HOST" 54 | dokku_log_info1 "Setting config vars for $TARGET" 55 | dokku config:set --no-restart "$TARGET" $(env_for "$SOURCE") &> /dev/null 56 | } 57 | 58 | unset_link_and_env_for() { 59 | local APP="$1" 60 | dokku docker-options:remove "$APP" build,deploy,run "--link $PSQL_CONTAINER_NAME:$PSQL_HOST" 61 | dokku_log_info1 "Unsetting config vars for $APP" 62 | dokku config:unset --no-restart "$APP" DATABASE_URL POSTGRESQL_URL DB_TYPE DB_HOST DB_PORT DB_NAME DB_USER DB_PASS &> /dev/null 63 | } 64 | 65 | export_psql_image() { 66 | PSQL_IMAGE="$(dokku config:get --global PSQL_SC_IMAGE)" || true 67 | PSQL_IMAGE="${PSQL_IMAGE:-postgres:9.3}" 68 | export PSQL_IMAGE 69 | } 70 | 71 | export_psql_root() { 72 | PSQL_ROOT="$(dokku config:get --global PSQL_SC_ROOT)" || true 73 | PSQL_ROOT="${PSQL_ROOT:-$DOKKU_ROOT/.psql-sc}" 74 | export PSQL_ROOT 75 | } 76 | 77 | bind_ip_options() { 78 | local BIND_IP=$(dokku config:get --global PSQL_SC_BIND_IP) 79 | if [[ -n $BIND_IP ]]; then 80 | echo "-p $BIND_IP:$PSQL_PORT:$PSQL_PORT" 81 | else 82 | echo '' 83 | fi 84 | } 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dokku-psql-single-container 2 | 3 | dokku-psql-single-container is a plugin for [dokku][dokku] that provides a Postgresql server in a single container for your applications. 4 | 5 | It uses the official Postgresql docker image (version 9.3) by default but you can provide an alternative image. 6 | 7 | This version is compatible with dokku 0.3.26+. 8 | 9 | ## Installation 10 | 11 | ```shell 12 | # dokku 0.3.26 13 | $ git clone https://github.com/Flink/dokku-psql-single-container.git /var/lib/dokku/plugins/psql-sc 14 | $ dokku plugins-install 15 | 16 | # dokku 0.4+ 17 | $ dokku plugin:install https://github.com/Flink/dokku-psql-single-container.git 18 | ``` 19 | 20 | ### Providing an alternative docker image for PostgreSQL 21 | 22 | If you want to use an alternative image instead of the default `postgres:9.3` 23 | one, you just have to set it in the global dokku config **before** 24 | running `dokku plugins-install`. 25 | 26 | ```bash 27 | dokku config:set --global PSQL_SC_IMAGE=postgres:9.4 28 | ``` 29 | 30 | ### Setting a custom PSQL_ROOT 31 | 32 | By default, this plugin stores its data in `$DOKKU_ROOT/.psql-sc`. If you want 33 | to store it elsewhere, you have to set the `PSQL_SC_ROOT` in the global dokku 34 | config. You should do it **before** running `dokku plugins-install`. **Note:** 35 | If you are using custom `PSQL_SC_ROOT` directory, make sure it is writable by 36 | `dokku` user. 37 | 38 | ```bash 39 | dokku config:set --global PSQL_SC_ROOT=/my/custom/path 40 | ``` 41 | 42 | ### Binding Postgresql port to another IP 43 | 44 | By default, the container is not reachable from outside. The container can be 45 | bound to an IP of your choice by setting `PSQL_SC_BIND_IP` to an IP in the 46 | global dokku config. 47 | 48 | ```bash 49 | dokku config:set --global PSQL_SC_BIND_IP=0.0.0.0 50 | ``` 51 | 52 | ## Commands 53 | ``` 54 | $ dokku help 55 | psql:admin_console Launch a postgresql console as admin user 56 | psql:console Launch a postgresql console for 57 | psql:create Create a Postgresql database for 58 | psql:delete Delete Postgresql database for 59 | psql:dump > Dump database to PG dump format 60 | psql:link Link app DB to app 61 | psql:list List all databases 62 | psql:restart Restart the Postgresql docker container 63 | psql:restore < Restore database to from any non-plain-text format exported by pg_dump 64 | psql:start Start the Postgresql docker container if it isn't running 65 | psql:status Shows status of Postgresql 66 | psql:stop Stop the Postgresql docker container 67 | psql:unlink Remove DB config for 68 | psql:url Get DATABASE_URL for 69 | psql:version Output version of plugin 70 | ``` 71 | 72 | ## Info 73 | This plugin adds the following environment variables to your app automatically (they are available via `dokku config`): 74 | 75 | * DATABASE\_URL 76 | * DB\_HOST 77 | * DB\_NAME 78 | * DB\_PASS 79 | * DB\_PORT 80 | * DB\_TYPE 81 | * DB\_USER 82 | * POSTGRESQL\_URL 83 | 84 | ## Usage 85 | 86 | ### Start PostgreSQL: 87 | ``` 88 | $ dokku psql:start # Server side 89 | $ ssh dokku@server psql:start # Client side 90 | ``` 91 | 92 | ### Stop PostgreSQL: 93 | ``` 94 | $ dokku psql:stop # Server side 95 | $ ssh dokku@server psql:stop # Client side 96 | ``` 97 | 98 | ### Restart PostgreSQL: 99 | ``` 100 | $ dokku psql:restart # Server side 101 | $ ssh dokku@server psql:restart # Client side 102 | ``` 103 | 104 | ### Create a new database for an existing app: 105 | ``` 106 | $ dokku psql:create foo # Server side 107 | $ ssh dokku@server psql:create foo # Client side 108 | ``` 109 | 110 | ### Dump database: 111 | ``` 112 | $ dokku psql:dump foo > filename.dump # Server side 113 | ``` 114 | 115 | ### Restore database from dump: 116 | ``` 117 | $ dokku psql:restore foo < filename.dump # Server side 118 | ``` 119 | 120 | For plain-text format, you should do this instead: 121 | 122 | ```shell 123 | $ dokku psql:console < dump.sql 124 | ``` 125 | 126 | ### Copy database foo to database bar using pipe: 127 | ``` 128 | $ dokku psql:dump foo | dokku psql:restore bar # Server side 129 | ``` 130 | 131 | ### Link an existing DB to another application 132 | ``` 133 | $ dokku psql:link app_with_db target_app # Server side 134 | ``` 135 | 136 | ### Unlink an application 137 | ``` 138 | $ dokku psql:unlink foo # Server side 139 | ``` 140 | 141 | If you inadvertently unlink an app owning a DB, there is a very easy way to 142 | recover the configuration vars: 143 | 144 | ``` 145 | $ dokku psql:link foo foo # Server side 146 | ``` 147 | 148 | ## Known issues 149 | 150 | When upgrading Dokku to a newer version, owner of `/home/dokku/.psql-sc/data` will be changed to `dokku`. It can causes errors with the container. 151 | 152 | You’ll have to restore the previous owner by issuing the following command: 153 | ``` 154 | $ chown 999 -R /home/dokku/.psql-sc/data 155 | ``` 156 | 157 | ## Acknowledgements 158 | 159 | This plugin is based originally on the [one by Olivier Hardy](https://github.com/ohardy/dokku-psql). 160 | 161 | ## License 162 | 163 | This plugin is released under the MIT license. See the file [LICENSE](LICENSE). 164 | 165 | [dokku]: https://github.com/progrium/dokku 166 | -------------------------------------------------------------------------------- /commands: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x 3 | 4 | PLUGIN_BASE_PATH="$PLUGIN_PATH" 5 | if [[ -n $DOKKU_API_VERSION ]]; then 6 | PLUGIN_BASE_PATH="$PLUGIN_ENABLED_PATH" 7 | fi 8 | source "$PLUGIN_BASE_PATH/common/functions" 9 | source "$(dirname $0)/functions" 10 | set +e 11 | 12 | if [[ $1 == psql:* ]]; then 13 | export_psql_image 14 | export_psql_root 15 | load_container_id 16 | fi 17 | 18 | case "$1" in 19 | psql:admin_console) 20 | check_container 21 | has_tty && TTY_OPTS="-t" 22 | docker exec -i $TTY_OPTS "$PSQL_CONTAINER_NAME" env TERM="$TERM" psql -h localhost -U postgres 23 | ;; 24 | 25 | psql:console) 26 | check_container; check_app; verify_app_name "$APP"; check_exists 27 | DATABASE=$(cat "$PSQL_ROOT/db_$APP") 28 | PASSWORD=$(cat "$PSQL_ROOT/pass_$APP") 29 | has_tty && TTY_OPTS="-t" 30 | docker exec -i $TTY_OPTS "$PSQL_CONTAINER_NAME" env TERM="$TERM" PGPASSWORD="$PASSWORD" psql -h localhost -U "$DATABASE" "$DATABASE" 31 | ;; 32 | 33 | psql:url) 34 | check_container; check_app; verify_app_name "$APP"; check_exists 35 | db_url "$APP" 36 | ;; 37 | 38 | psql:create) 39 | check_container; check_app; verify_app_name "$APP"; check_already_exists 40 | PASSWORD=$(openssl rand -hex 32) 41 | DATABASE=$(database_name "$APP") 42 | echo "$DATABASE" > "$PSQL_ROOT/db_$APP" 43 | echo "$PASSWORD" > "$PSQL_ROOT/pass_$APP" 44 | chmod 600 "$PSQL_ROOT/db_$APP" "$PSQL_ROOT/pass_$APP" 45 | dokku_log_info1 "Creating database $APP" 46 | docker exec "$PSQL_CONTAINER_NAME" su - postgres -c "psql --command \"CREATE USER $DATABASE WITH PASSWORD '$PASSWORD';\";\ 47 | createdb -E utf8 -O $DATABASE $DATABASE;\ 48 | psql --command \"GRANT ALL PRIVILEGES ON DATABASE $DATABASE TO $DATABASE;\"" > /dev/null 49 | set_link_and_env_for "$APP" 50 | ;; 51 | 52 | psql:delete) 53 | check_container; check_app; check_exists 54 | DATABASE=$(cat "$PSQL_ROOT/db_$APP") 55 | dokku_log_info1 "Deleting database $APP" 56 | docker exec "$PSQL_CONTAINER_NAME" su - postgres -c "dropdb $DATABASE; dropuser $DATABASE" > /dev/null 57 | rm -f "$PSQL_ROOT/db_$APP" "$PSQL_ROOT/pass_$APP" 58 | if [[ -d "$DOKKU_ROOT/$APP" ]]; then 59 | unset_link_and_env_for "$APP" 60 | fi 61 | ;; 62 | 63 | psql:list) 64 | check_container 65 | docker exec -i "$PSQL_CONTAINER_NAME" env TERM="$TERM" psql -h localhost -U postgres -c '\l' 66 | ;; 67 | 68 | psql:start) 69 | if [[ -n "$PSQL_ID" ]]; then 70 | echo "Postgresql container already running with ID: $PSQL_ID" 71 | exit 1 72 | fi 73 | OLD_ID=$(docker ps -f status=exited | grep "$PSQL_CONTAINER_NAME" | awk '{print $1}') 74 | if [[ -n $OLD_ID ]]; then 75 | dokku_log_info1 "Restarting previously stopped Postgresql container $OLD_ID" 76 | docker start "$OLD_ID" > /dev/null 77 | exit 0 78 | fi 79 | dokku_log_info1 "Starting Postgresql server" 80 | docker run $(bind_ip_options) -d -v "$PSQL_ROOT/data:/var/lib/postgresql/data" --name "$PSQL_CONTAINER_NAME" "$PSQL_IMAGE" > /dev/null 81 | ;; 82 | 83 | psql:restart) 84 | dokku psql:stop 85 | dokku psql:start 86 | ;; 87 | 88 | psql:stop) 89 | check_container 90 | dokku_log_info1 "Stopping Postgresql server" 91 | docker stop "$PSQL_CONTAINER_NAME" > /dev/null 92 | ;; 93 | 94 | psql:dump) 95 | check_container; check_app; verify_app_name "$APP"; check_exists 96 | DATABASE=$(cat "$PSQL_ROOT/db_$APP") 97 | PASSWORD=$(cat "$PSQL_ROOT/pass_$APP") 98 | docker exec "$PSQL_CONTAINER_NAME" env PGPASSWORD="$PASSWORD" pg_dump -Fc --no-acl --no-owner -h localhost -U "$DATABASE" -w "$DATABASE" 99 | ;; 100 | 101 | psql:restore) 102 | check_container; check_app; verify_app_name "$APP"; check_exists 103 | DATABASE=$(cat "$PSQL_ROOT/db_$APP") 104 | PASSWORD=$(cat "$PSQL_ROOT/pass_$APP") 105 | if [[ -t 0 ]]; then 106 | echo "No data provided on stdin." && exit 1 107 | fi 108 | docker exec -i "$PSQL_CONTAINER_NAME" env PGPASSWORD="$PASSWORD" pg_restore -h localhost -cO -d "$DATABASE" -U "$DATABASE" -w 109 | ;; 110 | 111 | psql:status) 112 | [[ -n "$PSQL_ID" ]] && echo "Postgresql container running with ID: $PSQL_ID" && exit 0 113 | echo "Postgresql container not running" 114 | ;; 115 | 116 | psql:link) 117 | check_container; check_app; verify_app_name "$APP"; check_exists 118 | TARGET_APP="$3" 119 | [[ -z $TARGET_APP ]] && echo "Target app is missing" && exit 1 120 | verify_app_name "$TARGET_APP" 121 | set_link_and_env_for "$APP" "$TARGET_APP" 122 | ;; 123 | 124 | psql:unlink) 125 | check_app; verify_app_name "$APP" 126 | unset_link_and_env_for "$APP" 127 | ;; 128 | 129 | psql:version) 130 | echo -n "v" 131 | cat "$(dirname "$0")/VERSION" 132 | ;; 133 | 134 | help) 135 | HELP=$(cat<, Launch a postgresql console for 137 | psql:url , Get DATABASE_URL for 138 | psql:create , Create a Postgresql database for 139 | psql:delete , Delete Postgresql database for 140 | psql:link , Link app DB to app 141 | psql:unlink , Remove DB config for 142 | psql:dump > , Dump database to PG dump format 143 | psql:restore < , Restore database to from any non-plain-text format exported by pg_dump 144 | psql:admin_console, Launch a postgresql console as admin user 145 | psql:restart, Restart the Postgresql docker container 146 | psql:start, Start the Postgresql docker container if it isn't running 147 | psql:stop, Stop the Postgresql docker container 148 | psql:status, Shows status of Postgresql 149 | psql:list, List all databases 150 | psql:version, Output version of plugin 151 | EOF 152 | ) 153 | if [[ -n $DOKKU_API_VERSION ]]; then 154 | echo "$HELP" 155 | else 156 | cat && echo "$HELP" 157 | fi 158 | ;; 159 | esac 160 | --------------------------------------------------------------------------------