├── .github └── workflows │ └── docker-publish.yml ├── Dockerfile ├── README.md ├── examples └── ttrss_stack.yaml └── root ├── etc ├── cont-init.d │ ├── 50-php │ ├── 98-wait-for-db │ └── 99-ttrss ├── nginx │ └── nginx.conf └── services.d │ ├── .s6-svscan │ └── finish │ ├── nginx │ └── run │ ├── php │ └── run │ ├── ttrss-daemon │ └── run │ └── ttrss-updater │ └── run └── srv ├── setup-ttrss.sh ├── start-ttrss.sh ├── ttrss-configure-db.php ├── ttrss-configure-plugin-mobilize.php ├── ttrss-plugin-mobilize.mysql ├── ttrss-plugin-mobilize.pgsql ├── ttrss-utils.php └── update-ttrss.sh /.github/workflows/docker-publish.yml: -------------------------------------------------------------------------------- 1 | name: Docker 2 | 3 | # This workflow uses actions that are not certified by GitHub. 4 | # They are provided by a third-party and are governed by 5 | # separate terms of service, privacy policy, and support 6 | # documentation. 7 | 8 | on: 9 | schedule: 10 | - cron: '30 10 * * *' 11 | push: 12 | branches: [ "master" ] 13 | # Publish semver tags as releases. 14 | tags: [ 'v*.*.*' ] 15 | pull_request: 16 | branches: [ "master" ] 17 | 18 | env: 19 | # Use docker.io for Docker Hub if empty 20 | REGISTRY: ghcr.io 21 | # github.repository as / 22 | IMAGE_NAME: ${{ github.repository }} 23 | 24 | 25 | jobs: 26 | build: 27 | 28 | runs-on: ubuntu-latest 29 | permissions: 30 | contents: read 31 | packages: write 32 | # This is used to complete the identity challenge 33 | # with sigstore/fulcio when running outside of PRs. 34 | id-token: write 35 | 36 | steps: 37 | - name: Checkout repository 38 | uses: actions/checkout@v3 39 | 40 | # Install the cosign tool except on PR 41 | # https://github.com/sigstore/cosign-installer 42 | - name: Install cosign 43 | if: github.event_name != 'pull_request' 44 | uses: sigstore/cosign-installer@6e04d228eb30da1757ee4e1dd75a0ec73a653e06 #v3.1.1 45 | with: 46 | cosign-release: 'v2.1.1' 47 | 48 | # Set up BuildKit Docker container builder to be able to build 49 | # multi-platform images and export cache 50 | # https://github.com/docker/setup-buildx-action 51 | - name: Set up Docker Buildx 52 | uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 53 | 54 | # Login against a Docker registry except on PR 55 | # https://github.com/docker/login-action 56 | - name: Log into registry ${{ env.REGISTRY }} 57 | if: github.event_name != 'pull_request' 58 | uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 59 | with: 60 | registry: ${{ env.REGISTRY }} 61 | username: ${{ github.actor }} 62 | password: ${{ secrets.GITHUB_TOKEN }} 63 | 64 | # Extract metadata (tags, labels) for Docker 65 | # https://github.com/docker/metadata-action 66 | - name: Extract Docker metadata 67 | id: meta 68 | uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0 69 | with: 70 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 71 | 72 | # Build and push Docker image with Buildx (don't push on PR) 73 | # https://github.com/docker/build-push-action 74 | - name: Build and push Docker image 75 | id: build-and-push 76 | uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 77 | with: 78 | context: . 79 | push: ${{ github.event_name != 'pull_request' }} 80 | tags: ${{ steps.meta.outputs.tags }} 81 | labels: ${{ steps.meta.outputs.labels }} 82 | cache-from: type=gha 83 | cache-to: type=gha,mode=max 84 | 85 | # Sign the resulting Docker image digest except on PRs. 86 | # This will only write to the public Rekor transparency log when the Docker 87 | # repository is public to avoid leaking data. If you would like to publish 88 | # transparency data even for private images, pass --force to cosign below. 89 | # https://github.com/sigstore/cosign 90 | - name: Sign the published Docker image 91 | if: ${{ github.event_name != 'pull_request' }} 92 | env: 93 | # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable 94 | TAGS: ${{ steps.meta.outputs.tags }} 95 | DIGEST: ${{ steps.build-and-push.outputs.digest }} 96 | # This step uses the identity token to provision an ephemeral certificate 97 | # against the sigstore community Fulcio instance. 98 | run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} 99 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Using https://github.com/gliderlabs/docker-alpine, 2 | # plus https://github.com/just-containers/s6-overlay for a s6 Docker overlay. 3 | FROM docker.io/alpine:3 AS builder 4 | # Initially was based on work of Christian Lück . 5 | LABEL description="A complete, self-hosted Tiny Tiny RSS (TTRSS) environment." \ 6 | maintainer="Andreas Löffler " 7 | 8 | # Note! When changing this version, also make sure to adapt the scripts which use / lookup the according binaries. 9 | ARG PHP_VER=84 10 | 11 | RUN set -xe && \ 12 | apk update && apk upgrade && \ 13 | apk add --no-cache --virtual=run-deps \ 14 | busybox nginx git ca-certificates curl \ 15 | php${PHP_VER} php${PHP_VER}-fpm php${PHP_VER}-phar \ 16 | php${PHP_VER}-pdo php${PHP_VER}-gd php${PHP_VER}-pgsql php${PHP_VER}-pdo_pgsql php${PHP_VER}-xmlwriter \ 17 | php${PHP_VER}-mbstring php${PHP_VER}-intl php${PHP_VER}-xml php${PHP_VER}-curl php${PHP_VER}-simplexml \ 18 | php${PHP_VER}-session php${PHP_VER}-tokenizer php${PHP_VER}-dom php${PHP_VER}-fileinfo php${PHP_VER}-ctype \ 19 | php${PHP_VER}-json php${PHP_VER}-iconv php${PHP_VER}-pcntl php${PHP_VER}-posix php${PHP_VER}-zip php${PHP_VER}-exif php${PHP_VER}-openssl \ 20 | tar xz 21 | 22 | # Add user www-data for php-fpm. 23 | # 82 is the standard uid/gid for "www-data" in Alpine. 24 | RUN adduser -u 82 -D -S -G www-data www-data 25 | 26 | # Copy root file system. 27 | COPY root / 28 | 29 | # Add s6 overlay. 30 | #ARG S6_OVERLAY_VERSION=3.1.5.0 31 | #RUN curl -L -s https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz | tar -Jxpf - -C / 32 | # Note: Tweak this line if you're running anything other than x86 AMD64 (64-bit). 33 | #RUN curl -L -s https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-x86_64.tar.xz | tar -Jxpf - -C / 34 | RUN curl -L -s https://github.com/just-containers/s6-overlay/releases/download/v1.19.1.1/s6-overlay-amd64.tar.gz | tar xvzf - -C / 35 | 36 | # Add wait-for-it.sh 37 | ADD https://raw.githubusercontent.com/Eficode/wait-for/master/wait-for /srv 38 | RUN chmod 755 /srv/wait-for 39 | 40 | # Expose Nginx ports. 41 | EXPOSE 8080 42 | EXPOSE 4443 43 | 44 | # Expose default database credentials via ENV in order to ease overwriting. 45 | ENV DB_NAME ttrss 46 | ENV DB_USER ttrss 47 | ENV DB_PASS ttrss 48 | 49 | # Clean up. 50 | RUN set -xe && apk del --progress --purge && rm -rf /var/cache/apk/* && rm -rf /var/lib/apt/lists/* 51 | 52 | ENTRYPOINT ["/init"] 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # No longer maintained due to lack of time. Consider using one of the other Docker-ized TTRSS setups. 2 | 3 | --- 4 | 5 | # docker-ttrss 6 | 7 | **The ready-to-use Docker image moved from Docker Hub to Github now: https://ghcr.io/x86dev/docker-ttrss** 8 | 9 | This Dockerfile installs Tiny Tiny RSS (TT-RSS) with the following features: 10 | 11 | - **New** Using PHP 8 now 12 | - **New** Latest Docker image is publish on Github directly now: https://ghcr.io/x86dev/docker-ttrss 13 | - Uses a dedicated volume for the Postgres database data 14 | - Integrated [Feedly-ish theme](https://github.com/Gravemind/tt-rss-feedlish-theme) for supporting latest TT-RSS versions 15 | - When starting the container, the default theme will be (re-)applied. This should help when a theme breaks (again) 16 | - Based on [Docker-Alpine](https://github.com/gliderlabs/docker-alpine) and [s6](http://skarnet.org/software/s6/) as the supervisor 17 | - Small and lightweight image size (< 100 MB) 18 | - Rolling release support: Updates TT-RSS automatically every day 19 | - Works nicely with jwilder's [nginx-proxy](https://github.com/jwilder/nginx-proxy), e.g. to use for Let's Encrypt SSL certificates 20 | - Integrated [Feedly theme](https://github.com/levito/tt-rss-feedly-theme) 21 | - Integrated [FeedIron plugin](https://github.com/m42e/ttrss_plugin-feediron) to get modify feeds 22 | - Integrated [Mobilize plugin](https://github.com/sepich/tt-rss-mobilize) for using Readability, Instapaper + Google Mobilizer 23 | - Optional: Self-signed 4096-bit RSA TLS certificate for accessing TT-RSS via https 24 | - Originally was based on [clue/docker-ttrss](https://github.com/clue/docker-ttrss) 25 | 26 | Feel free to tweak this further to your likings. 27 | 28 | This docker image allows you to run the [Tiny Tiny RSS](http://www.tt-rss.org) feed reader. 29 | Keep your feed history to yourself and access your RSS and atom feeds from everywhere. 30 | You can access it through an easy to use webinterface on your desktop, your mobile browser 31 | or using one of available apps. 32 | 33 | **Note: All commands must be executed as root!** 34 | 35 | ## Quickstart 36 | 37 | This section assumes you want to get started quickly, the following sections explain the 38 | steps in more detail. So let's start. 39 | 40 | Create a new database volume: 41 | 42 | ```bash 43 | # docker volume create ttrss-db 44 | ``` 45 | 46 | Create a Postgres database instance: 47 | 48 | ```bash 49 | # DB=$(docker run -d --restart=always --name ttrss-db -v ttrss-db:/var/lib/postgresql/data -e POSTGRES_PASSWORD=password postgres:alpine) 50 | ``` 51 | 52 | Next, run the actual TT-RSS instance by doing a: 53 | 54 | ```bash 55 | # docker run -d --link $DB:db -e TTRSS_PORT=8080 --name ttrss x86dev/docker-ttrss 56 | ``` 57 | 58 | Running this command for the first time will download the image automatically. 59 | 60 | 61 | ## Accessing your Tiny Tiny RSS (TT-RSS) 62 | 63 | The above example exposes the TT-RSS web interface on port 8080 (http / unencrypted), so that you can browse to: 64 | 65 | ```bash 66 | http://localhost:8080 67 | ``` 68 | 69 | The default login credentials are: 70 | 71 | ```bash 72 | Username: admin 73 | Password: password 74 | ``` 75 | 76 | Obviously, you're recommended to change those ASAP. 77 | See the next section about how to enable encryption support (via SSL/TLS). 78 | 79 | 80 | ## Use self-signed certificates (SSL/TLS) 81 | 82 | For enabling SSL/TLS support with a self-signed certificate you have to add `-e TTRSS_WITH_SELFSIGNED_CERT=1 -p 443:4443` 83 | when running your TT-RSS container. Then you can access TT-RSS via: `https://`. 84 | 85 | **Warning: Running services unencrypted on the Internet is not recommended!** 86 | 87 | The container also has been successfully tested with Let's Encrypt certificates. 88 | 89 | 90 | ## Reverse proxy support 91 | 92 | A nice thing to have is jwilder's [nginx-proxy](https://github.com/jwilder/nginx-proxy) as a separate 93 | Docker container running on the same machine as this one. 94 | 95 | That way you easily can integrate your TT-RSS instance with an existing domain by using a sub domain 96 | (e.g. https://ttrss.yourdomain.tld). 97 | 98 | ### Enabling SSL/TLS encryption support 99 | 100 | In combination with an official Let's Encrypt certificate you 101 | can get a nice A+ encryption/security rating over at [SSLLabs](https://www.ssllabs.com/ssltest/). 102 | 103 | 104 | ## Installation walkthrough 105 | 106 | ### Running 107 | 108 | Following Docker's best practices, this container does not contain its own database, 109 | but instead expects you to supply a running database instance. 110 | While slightly more complicated at first, this gives your more freedom as to which 111 | database instance and configuration you're relying on. 112 | Also, this makes this container quite disposable, as it doesn't store any sensitive 113 | information at all. 114 | 115 | 116 | ### Starting a database instance 117 | 118 | This container requires a PostgreSQL database instance. You're free to pick (or build) 119 | any, as long as is exposes its database port (5432) to the outside. 120 | 121 | Example: 122 | 123 | ```bash 124 | # docker volume create ttrss-db 125 | # docker run -d --restart=always --name=ttrss-db -v ttrss-db:/var/lib/postgresql/data -e POSTGRES_PASSWORD= postgres:alpine 126 | ``` 127 | 128 | Note: The above example creates a separate data volume where the actual Postgres database data lives in. 129 | 130 | ### Testing TT-RSS in foreground 131 | 132 | For testing purposes it's recommended to initially start this container in foreground. 133 | This is particular useful for your initial database setup, as errors get reported to 134 | the console and further execution will halt. 135 | 136 | ```bash 137 | # docker run -it -e TTRSS_PORT=8080 --link ttrss-db:db --name ttrss x86dev/docker-ttrss 138 | ``` 139 | 140 | ### Database configuration 141 | 142 | Whenever your run TT-RSS, it will check your database setup. It assumes the following 143 | default configuration, which can be changed by passing the following additional arguments: 144 | 145 | ```bash 146 | -e DB_NAME=ttrss 147 | -e DB_USER=ttrss 148 | -e DB_PASS=ttrss 149 | ``` 150 | 151 | By default, a PostgreSQL database is needed. 152 | 153 | #### Use a MySQL database 154 | 155 | Specify the following to use an existing MySQL database instead of a PostgreSQL one: 156 | ```bash 157 | -e DB_TYPE=mysql 158 | ``` 159 | 160 | ### Database user 161 | 162 | When you run TT-RSS it will check your database setup. If it can not connect using the above 163 | configuration, it will automatically try to create a new database and user. 164 | 165 | For this to work, it will need a superuser (root) account that is permitted to create a new database 166 | and user. It assumes the following default configuration, which can be changed by passing the 167 | following additional arguments: 168 | 169 | ```bash 170 | -e DB_ENV_USER=docker 171 | -e DB_ENV_PASS=docker 172 | ``` 173 | 174 | ### Running TT-RSS daemonized 175 | 176 | Once you've confirmed everything works in the foreground, you can start your container 177 | in the background by replacing the `-it` argument with `-d` (daemonize). 178 | Remaining arguments can be passed just like before, the following is the recommended 179 | minimum: 180 | 181 | ```bash 182 | # docker run -d --link ttrss-db:db --name ttrss x86dev/docker-ttrss 183 | ``` 184 | ## Useful stuff to know 185 | 186 | ### Single User mode 187 | In case you are running behind a proxy that has basic auth and you do not wish to use 188 | ttrss auth, you can set the enviroment variable TTRSS_SINGLEUSER to true and it 189 | will change the setup to single user mode 190 | 191 | ### Backing up / moving to another server 192 | 193 | Decided to back up your data container and/or move to another server? Here's how 194 | you do it: 195 | 196 | * On the old server, stop your TT-RSS container. 197 | * Back up the database volume (```ttrss-db```) and copy it to the new server. 198 | * Repeat the steps for creating the TT-RSS database container and the actual TT-RSS instance. 199 | This time the (old) database volume will be already around, so that you can start right away. 200 | 201 | ### Automatic updates 202 | 203 | When running this docker container you don't need to worry anymore how and when to 204 | update TT-RSS. Since TT-RSS has a so-called "rolling release" model since some time 205 | (which essentially means that there won't be any specific versions like 1.0, 1.1 etc), 206 | this container takes the burden any checks for updates of TT-RSS and the accompanied 207 | plugins/themes every day via an own update script (see `root/srv/update-ttrss.sh`). 208 | 209 | By default the update script checks every 24 hours if there are updates for TT-RSS, 210 | the plugins or the theme(s) available. 211 | 212 | If you want to change the update interval you just need to edit the file 213 | `root/etc/services.d/ttrss-updater/run` and change the `--wait-exit 24h` to fit your needs, whereas 214 | the suffix `h` stands for hours, `m` for minutes and `s` for seconds. 215 | 216 | 217 | ### Want to contribute? 218 | 219 | You think you have something which absolutely must be part of this container, implemented 220 | a cool new feature or fixed some nasty bug? Let me know and send me a git pull request. 221 | 222 | The repository can be found [here](https://github.com/x86dev/docker-ttrss). 223 | -------------------------------------------------------------------------------- /examples/ttrss_stack.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Provides service over TLS via proxy as a subdomain 3 | # Implements x86dev/docker-ttrss with external postgres and jwilder/nginx-proxy + jrcs/letsencrypt-nginx-proxy-companion 4 | # Usage: run with 5 | # docker stack deploy -c ttrss_stack.yaml ttrss 6 | version: '3.1' 7 | 8 | services: 9 | db: 10 | image: postgres 11 | restart: always 12 | volumes: 13 | - db:/var/lib/postgresql/data 14 | environment: 15 | - POSTGRES_PASSWORD=XXXXXXXX 16 | 17 | ttrss: 18 | image: x86dev/docker-ttrss 19 | restart: always 20 | volumes: 21 | - db:/var/lib/postgresql/data 22 | environment: 23 | - DB_ENV_PASSWORD=XXXXXXXX 24 | - DB_ENV_USER=postgres 25 | - DB_HOST=db 26 | - DB_NAME=ttrss 27 | - DB_PASSWORD=XXXXXXXX 28 | - DB_PORT=tcp://db:5432 29 | - DB_PORT_5432_TCP=tcp://db:5432 30 | - DB_PORT_5432_TCP_ADDR=db 31 | - DB_PORT_5432_TCP_PORT=5432 32 | - DB_PORT_5432_TCP_PROTO=tcp 33 | - DB_USER=ttrss 34 | - POSTGRES_PASSWORD=XXXXXXXX 35 | - SELF_URL_PATH=https://ttrss.mydomain.de/ 36 | - TTRSS_PORT=8080 37 | - TTRSS_SELF_URL=https://ttrss.mydomain.de/ 38 | # - TTRSS_PROTO=http 39 | - TTRSS_URL=ttrss.mydomain.de 40 | - VIRTUAL_HOST=ttrss.mydomain.de 41 | - VIRTUAL_PORT=8080 42 | - LETSENCRYPT_HOST=ttrss.mydomain.de 43 | - LETSENCRYPT_EMAIL=XXXXXXXXXXXXXXXXXX 44 | depends_on: 45 | - db 46 | networks: 47 | - proxy-tier 48 | - default 49 | 50 | proxy: 51 | image: jwilder/nginx-proxy:alpine 52 | restart: always 53 | ports: 54 | - 80:80 55 | - 443:443 56 | labels: 57 | com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy: "true" 58 | volumes: 59 | - certs:/etc/nginx/certs:ro 60 | - conf.d:/etc/nginx/conf.d 61 | - vhost.d:/etc/nginx/vhost.d 62 | - html:/usr/share/nginx/html 63 | - /var/run/docker.sock:/tmp/docker.sock:ro 64 | - ./nextcloud/uploadsize.conf:/etc/nginx/conf.d/uploadsize.conf:ro 65 | networks: 66 | - proxy-tier 67 | 68 | 69 | letsencrypt-companion: 70 | image: jrcs/letsencrypt-nginx-proxy-companion 71 | restart: always 72 | volumes: 73 | - certs:/etc/nginx/certs 74 | - conf.d:/etc/nginx/conf.d 75 | - vhost.d:/etc/nginx/vhost.d 76 | - html:/usr/share/nginx/html 77 | - /var/run/docker.sock:/var/run/docker.sock:ro 78 | networks: 79 | - proxy-tier 80 | depends_on: 81 | - proxy 82 | 83 | volumes: 84 | db: 85 | certs: 86 | conf.d: 87 | vhost.d: 88 | html: 89 | 90 | networks: 91 | proxy-tier: 92 | -------------------------------------------------------------------------------- /root/etc/cont-init.d/50-php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv sh 2 | 3 | # Make sure to have a defined set of PHP binaries, 4 | # regardless of how the package maintainer(s) named those. 5 | 6 | rm /usr/bin/php 7 | ln -s /usr/bin/php84 /usr/bin/php 8 | 9 | rm /usr/sbin/php-fpm 10 | ln -s /usr/sbin/php-fpm84 /usr/sbin/php-fpm 11 | -------------------------------------------------------------------------------- /root/etc/cont-init.d/98-wait-for-db: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv sh 2 | 3 | # Wait for the DB to be reachable before proceeding. This avoids race condition 4 | # whereby database is not ready (yet), and so TTRSS config isn't correctly created 5 | 6 | ############# 7 | # This simply duplicates the logic from ttrss-configure-db.php 8 | ename='DB'; 9 | eport=5432; 10 | 11 | if [ "$DB_TYPE" = 'mysql' ]; 12 | then 13 | eport=3306; 14 | dbhost=$DB_PORT_3306_TCP_ADDR 15 | else 16 | eport=5432; 17 | dbhost=$DB_PORT_5432_TCP_ADDR 18 | fi 19 | ############# 20 | 21 | # Run wait-for to confirm DB comes up before we proceed 22 | # Reduce default timeout to 1s because if the DB is not instantly available, 23 | # Then it's even if it _happens_ to become available while we're testing, 24 | # it won't be ready for us yet. Better to exit and restart the container altogether, 25 | # So set "S6_BEHAVIOUR_IF_STAGE2_FAILS=2" in docker-compose.yml if you need this (i.e., in swarm mode, where dependencies don't work) 26 | 27 | echo "Checking database responds within 1s on $dbhost:$eport..." 28 | /srv/wait-for $dbhost:$eport --timeout=1 29 | -------------------------------------------------------------------------------- /root/etc/cont-init.d/99-ttrss: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv sh 2 | cd /srv && ./setup-ttrss.sh 3 | -------------------------------------------------------------------------------- /root/etc/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | user www-data; 2 | worker_processes auto; 3 | pid /tmp/nginx.pid; 4 | daemon off; 5 | 6 | events { 7 | worker_connections 1024; 8 | use epoll; 9 | } 10 | 11 | http { 12 | include /etc/nginx/mime.types; 13 | default_type application/octet-stream; 14 | 15 | access_log /var/log/nginx/access.log; 16 | error_log /var/log/nginx/error.log debug; 17 | 18 | sendfile on; 19 | keepalive_timeout 15; 20 | keepalive_disable msie6; 21 | keepalive_requests 100; 22 | tcp_nopush on; 23 | tcp_nodelay on; 24 | server_tokens off; 25 | 26 | fastcgi_temp_path /tmp/fastcgi 1 2; 27 | client_body_temp_path /tmp/client_body 1 2; 28 | proxy_temp_path /tmp/proxy 1 2; 29 | uwsgi_temp_path /tmp/uwsgi 1 2; 30 | scgi_temp_path /tmp/scgi 1 2; 31 | 32 | gzip off; 33 | 34 | server 35 | { 36 | listen 4443; 37 | root /var/www/ttrss; 38 | 39 | ssl on; 40 | ssl_certificate /etc/ssl/certs/ttrss.crt; 41 | ssl_certificate_key /etc/ssl/private/ttrss.key; 42 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 43 | ssl_prefer_server_ciphers on; 44 | ssl_ciphers "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA"; 45 | 46 | index index.php index.html; 47 | client_max_body_size 100M; 48 | 49 | location / { 50 | try_files $uri $uri/ =404; 51 | } 52 | 53 | location ~ \.php$ { 54 | fastcgi_split_path_info ^(.*\.php)(/.*)?$; 55 | fastcgi_pass unix:/tmp/php-fpm.sock; 56 | fastcgi_index index.php; 57 | include fastcgi_params; 58 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 59 | fastcgi_intercept_errors off; 60 | fastcgi_buffer_size 16k; 61 | fastcgi_buffers 4 16k; 62 | } 63 | 64 | location ~ /\.ht { 65 | deny all; 66 | } 67 | 68 | location ~ \.php$ { 69 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 70 | fastcgi_pass unix:/tmp/php-fpm.sock; 71 | fastcgi_index index.php; 72 | include fastcgi_params; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /root/etc/services.d/.s6-svscan/finish: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exit 0 -------------------------------------------------------------------------------- /root/etc/services.d/nginx/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv sh 2 | exec nginx 3 | -------------------------------------------------------------------------------- /root/etc/services.d/php/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv sh 2 | exec php-fpm 3 | -------------------------------------------------------------------------------- /root/etc/services.d/ttrss-daemon/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv sh 2 | 3 | while true; do 4 | cd /var/www/ttrss 5 | exec s6-setuidgid www-data php -f /var/www/ttrss/update_daemon2.php 6 | sleep 5m 7 | done 8 | -------------------------------------------------------------------------------- /root/etc/services.d/ttrss-updater/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv sh 2 | sleep 24h 3 | exec s6-setuidgid www-data /srv/update-ttrss.sh --wait-exit 24h 4 | -------------------------------------------------------------------------------- /root/srv/setup-ttrss.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | setup_nginx() 4 | { 5 | if [ -z "$TTRSS_HOST" ]; then 6 | TTRSS_HOST=ttrss 7 | fi 8 | 9 | NGINX_CONF=/etc/nginx/nginx.conf 10 | 11 | if [ "$TTRSS_WITH_SELFSIGNED_CERT" = "1" ]; then 12 | # Install OpenSSL. 13 | apk update && apk add openssl 14 | 15 | if [ ! -f "/etc/ssl/private/ttrss.key" ]; then 16 | echo "Setup: Generating self-signed certificate ..." 17 | # Generate the TLS certificate for our Tiny Tiny RSS server instance. 18 | openssl req -new -newkey rsa:4096 -days 3650 -nodes -x509 \ 19 | -subj "/C=US/ST=World/L=World/O=$TTRSS_HOST/CN=$TTRSS_HOST" \ 20 | -keyout "/etc/ssl/private/ttrss.key" \ 21 | -out "/etc/ssl/certs/ttrss.crt" 22 | fi 23 | 24 | # Turn on SSL. 25 | sed -i -e "s/listen\s*8080\s*;/listen 4443;/g" ${NGINX_CONF} 26 | sed -i -e "s/ssl\s*off\s*;/ssl on;/g" ${NGINX_CONF} 27 | sed -i -e "s/#ssl_/ssl_/g" ${NGINX_CONF} 28 | 29 | # Set permissions. 30 | chmod 600 "/etc/ssl/private/ttrss.key" 31 | chmod 600 "/etc/ssl/certs/ttrss.crt" 32 | else 33 | echo "Setup: !!! WARNING - No encryption (TLS) used - WARNING !!!" 34 | echo "Setup: !!! This is not recommended for a production server !!!" 35 | echo "Setup: You have been warned." 36 | 37 | # Turn off SSL. 38 | sed -i -e "s/listen\s*4443\s*;/listen 8080;/g" ${NGINX_CONF} 39 | sed -i -e "s/ssl\s*on\s*;/ssl off;/g" ${NGINX_CONF} 40 | sed -i -e "s/ssl_/#ssl_/g" ${NGINX_CONF} 41 | fi 42 | } 43 | 44 | setup_ttrss() 45 | { 46 | if [ -z "$TTRSS_REPO_URL" ]; then 47 | TTRSS_REPO_URL=https://git.tt-rss.org/fox/tt-rss.git/ 48 | fi 49 | 50 | if [ -z "$TTRSS_PATH" ]; then 51 | TTRSS_PATH=/var/www/ttrss 52 | fi 53 | 54 | TTRSS_PATH_THEMES=${TTRSS_PATH}/themes.local 55 | TTRSS_PATH_PLUGINS=${TTRSS_PATH}/plugins.local 56 | 57 | if [ ! -d ${TTRSS_PATH} ]; then 58 | mkdir -p ${TTRSS_PATH} 59 | if [ -n "$TTRSS_GIT_TAG" ]; then 60 | echo "Setup: Setting up Tiny Tiny RSS '$TTRSS_GIT_TAG' ..." 61 | cd ${TTRSS_PATH} 62 | git init . 63 | git fetch --depth=1 ${TTRSS_REPO_URL} refs/tags/${TTRSS_GIT_TAG}:refs/tags/${TTRSS_GIT_TAG} 64 | git checkout tags/${TTRSS_GIT_TAG} 65 | else 66 | echo "Setup: Setting up Tiny Tiny RSS (latest revision) ..." 67 | git clone --depth=1 ${TTRSS_REPO_URL} ${TTRSS_PATH} 68 | fi 69 | 70 | mkdir -p ${TTRSS_PATH_PLUGINS} 71 | git clone --depth=1 https://github.com/sepich/tt-rss-mobilize.git ${TTRSS_PATH_PLUGINS}/mobilize 72 | git clone --depth=1 https://github.com/feediron/ttrss_plugin-feediron.git ${TTRSS_PATH_PLUGINS}/feediron 73 | 74 | mkdir -p ${TTRSS_PATH_THEMES} 75 | git clone --depth=1 https://github.com/levito/tt-rss-feedly-theme.git ${TTRSS_PATH_THEMES}/levito-feedly-git 76 | git clone --depth=1 https://github.com/Gravemind/tt-rss-feedlish-theme.git ${TTRSS_PATH_THEMES}/gravemind-feedly-git 77 | fi 78 | 79 | # Add initial config. 80 | cp ${TTRSS_PATH}/config.php-dist ${TTRSS_PATH}/config.php 81 | 82 | # Check if TTRSS_URL is undefined, and if so, use localhost as default. 83 | if [ -z ${TTRSS_URL} ]; then 84 | TTRSS_URL=localhost 85 | fi 86 | 87 | if [ "$TTRSS_WITH_SELFSIGNED_CERT" = "1" ]; then 88 | # Make sure the TTRSS protocol is https now. 89 | TTRSS_PROTO=https 90 | fi 91 | 92 | # If no protocol is specified, use http as default. Not secure, I know. 93 | if [ -z ${TTRSS_PROTO} ]; then 94 | TTRSS_PROTO=http 95 | fi 96 | 97 | # Add a leading colon (for the final URL) to the port. 98 | if [ -n "$TTRSS_PORT" ]; then 99 | TTRSS_PORT=:${TTRSS_PORT} 100 | fi 101 | 102 | # If we've been passed $TTRSS_SELF_URL as an env variable, then use that, 103 | # otherwise use the URL we constructed above. 104 | if [ -z "$TTRSS_SELF_URL" ]; then 105 | # Construct the final URL TTRSS will use. 106 | TTRSS_SELF_URL=${TTRSS_PROTO}://${TTRSS_URL}${TTRSS_PORT}/ 107 | fi 108 | 109 | echo "Setup: URL is: $TTRSS_SELF_URL" 110 | 111 | # By default we want to reset the theme to the default one. 112 | if [ -z ${TTRSS_THEME_RESET} ]; then 113 | TTRSS_THEME_RESET=1 114 | fi 115 | 116 | # Patch URL path. 117 | echo "putenv('TTRSS_SELF_URL_PATH=$TTRSS_SELF_URL');" >> ${TTRSS_PATH}/config.php 118 | 119 | # Check if single user mode is selected 120 | if [ "$TTRSS_SINGLEUSER" = true ]; then 121 | echo "Single User mode Selected" 122 | echo "putenv('TTRSS_SINGLE_USER_MODE=1');" >> ${TTRSS_PATH}/config.php 123 | fi 124 | 125 | # Enable additional system plugins. 126 | if [ -z ${TTRSS_PLUGINS} ]; then 127 | 128 | TTRSS_PLUGINS= 129 | 130 | # Only if SSL/TLS is enabled: af_zz_imgproxy (Loads insecure images via built-in proxy). 131 | if [ "$TTRSS_PROTO" = "https" ]; then 132 | TTRSS_PLUGINS=${TTRSS_PLUGINS}af_zz_imgproxy 133 | fi 134 | fi 135 | 136 | echo "Setup: Additional plugins: $TTRSS_PLUGINS" 137 | 138 | sed -i -e "s/.*define('PLUGINS'.*/define('PLUGINS', '$TTRSS_PLUGINS, auth_internal, note, updater');/g" ${TTRSS_PATH}/config.php 139 | 140 | # Export variables for sub shells. 141 | export TTRSS_PATH 142 | export TTRSS_PATH_PLUGINS 143 | export TTRSS_THEME_RESET 144 | } 145 | 146 | setup_db() 147 | { 148 | echo "Setup: Database" 149 | php -f /srv/ttrss-configure-db.php 150 | php -f /srv/ttrss-configure-plugin-mobilize.php 151 | } 152 | 153 | setup_nginx 154 | setup_ttrss 155 | setup_db 156 | 157 | echo "Setup: Applying updates ..." 158 | /srv/update-ttrss.sh --no-start 159 | 160 | echo "Setup: Done" 161 | -------------------------------------------------------------------------------- /root/srv/start-ttrss.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | # Call the setup script to make sure everything is ready to go. 6 | /srv/setup-ttrss.sh --no-start 7 | 8 | # Call the image's init script which in turn calls the s6 supervisor then. 9 | /init 10 | -------------------------------------------------------------------------------- /root/srv/ttrss-configure-db.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | exec('CREATE ROLE ' . ($config['DB_USER']) . ' WITH LOGIN PASSWORD ' . $pdo->quote($config['DB_PASS'])); 51 | $pdo->exec('CREATE DATABASE ' . ($config['DB_NAME']) . ' WITH OWNER ' . ($config['DB_USER'])); 52 | unset($pdo); 53 | 54 | if (dbcheck($config)) { 55 | echo 'Database login created and confirmed' . PHP_EOL; 56 | } else { 57 | error('Database login failed, trying to create login failed as well'); 58 | } 59 | } 60 | 61 | $pdo = dbconnect($config); 62 | try { 63 | $pdo->query('SELECT 1 FROM ttrss_feeds'); 64 | echo 'Connection to database successful' . PHP_EOL; 65 | // Reached this point => table found, assume db is complete 66 | 67 | if (env('TTRSS_THEME_RESET', '1')) { 68 | // Make sure to set the default theme provided by TT-RSS. 69 | // Other themes might break everything after an update, so play safe here. 70 | echo 'Resetting theme to default ...' . PHP_EOL; 71 | $pdo->query("UPDATE ttrss_user_prefs SET value = '' WHERE pref_name = 'USER_CSS_THEME'"); 72 | } 73 | } 74 | catch (PDOException $e) { 75 | echo 'Database table not found, applying schema... ' . PHP_EOL; 76 | 77 | $schema = file_get_contents($confpath . '/schema/ttrss_schema_' . $config['DB_TYPE'] . '.sql'); 78 | $schema = preg_replace('/--(.*?);/', '', $schema); 79 | $schema = preg_replace('/[\r\n]/', ' ', $schema); 80 | $schema = trim($schema, ' ;'); 81 | foreach (explode(';', $schema) as $stm) { 82 | $pdo->exec($stm); 83 | } 84 | unset($pdo); 85 | } 86 | 87 | $contents = file_get_contents($conffile); 88 | foreach ($config as $name => $value) { 89 | $contents .= "putenv('TTRSS_" . $name . "=" . $value . "');\n"; 90 | } 91 | file_put_contents($conffile, $contents); 92 | -------------------------------------------------------------------------------- /root/srv/ttrss-configure-plugin-mobilize.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | query('SELECT 1 FROM plugin_mobilize_feeds'); 36 | // reached this point => table found, assume db is complete 37 | } 38 | catch (PDOException $e) { 39 | echo 'Database table for mobilize plugin not found, applying schema... ' . PHP_EOL; 40 | $schema = file_get_contents('/srv/ttrss-plugin-mobilize.'.$db_type); 41 | $schema = preg_replace('/--(.*?);/', '', $schema); 42 | $schema = preg_replace('/[\r\n]/', ' ', $schema); 43 | $schema = trim($schema, ' ;'); 44 | foreach (explode(';', $schema) as $stm) { 45 | $pdo->exec($stm); 46 | } 47 | unset($pdo); 48 | } 49 | 50 | $contents = file_get_contents($confpath); 51 | foreach ($config as $name => $value) { 52 | $contents = preg_replace('/(define\s*\(\'' . $name . '\',\s*)(.*)(\);)/', '$1"' . $value . '"$3', $contents); 53 | } 54 | file_put_contents($confpath, $contents); 55 | -------------------------------------------------------------------------------- /root/srv/ttrss-plugin-mobilize.mysql: -------------------------------------------------------------------------------- 1 | CREATE TABLE plugin_mobilize_feeds ( id int NOT NULL, owner_uid int NOT NULL, mobilizer_id int NOT NULL, PRIMARY KEY (id,owner_uid) ); 2 | CREATE TABLE plugin_mobilize_mobilizers ( id int NOT NULL, description varchar(255) NOT NULL, url varchar(1000) NOT NULL, PRIMARY KEY (id) ) ; 3 | 4 | INSERT INTO plugin_mobilize_mobilizers ( id, description, url) VALUES 5 | (0, 'Readability', 'http://www.readability.com/m?url=%s'), 6 | (1, 'Instapaper', 'http://www.instapaper.com/m?u=%s'), 7 | (2, 'Google Mobilizer', 'http://www.google.com/gwt/x?u=%s'), 8 | (3, 'Original Stripped', 'http://strip=%s'), 9 | (4, 'Original', '%s'); 10 | -------------------------------------------------------------------------------- /root/srv/ttrss-plugin-mobilize.pgsql: -------------------------------------------------------------------------------- 1 | CREATE TABLE "plugin_mobilize_feeds" ( "id" int NOT NULL, "owner_uid" int NOT NULL, "mobilizer_id" int NOT NULL, PRIMARY KEY ("id","owner_uid") ); 2 | CREATE TABLE "plugin_mobilize_mobilizers" ( "id" int NOT NULL, "description" varchar(255) NOT NULL, "url" varchar(1000) NOT NULL, PRIMARY KEY ("id") ) ; 3 | 4 | INSERT INTO "plugin_mobilize_mobilizers" ( "id", "description", "url") VALUES 5 | (0, 'Readability', 'http://www.readability.com/m?url=%s'), 6 | (1, 'Instapaper', 'http://www.instapaper.com/m?u=%s'), 7 | (2, 'Google Mobilizer', 'http://www.google.com/gwt/x?u=%s'), 8 | (3, 'Original Stripped', 'http://strip=%s'), 9 | (4, 'Original', '%s'); 10 | -------------------------------------------------------------------------------- /root/srv/ttrss-utils.php: -------------------------------------------------------------------------------- 1 | 'HOST', 'port' => 'PORT', 'dbname' => 'NAME', 'user' => 'USER', 'password' => 'PASS'); 23 | $dsn = $config['DB_TYPE'] . ':'; 24 | foreach ($map as $d => $h) { 25 | if (isset($config['DB_' . $h])) { 26 | $dsn .= $d . '=' . $config['DB_' . $h] . ';'; 27 | } 28 | } 29 | echo($dsn); 30 | if ($config['DB_TYPE'] == 'pgsql'){ 31 | $pdo = new \PDO($dsn); 32 | } else { 33 | $pdo = new \PDO($dsn, $config['DB_USER'], $config['DB_PASS']); 34 | } 35 | $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 36 | return $pdo; 37 | } 38 | 39 | function dbcheck($config) 40 | { 41 | try { 42 | dbconnect($config); 43 | return true; 44 | } 45 | catch (PDOException $e) { 46 | return false; 47 | } 48 | } 49 | 50 | ?> 51 | -------------------------------------------------------------------------------- /root/srv/update-ttrss.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | TTRSS_PATH=/var/www/ttrss 4 | TTRSS_PATH_THEMES=${TTRSS_PATH}/themes.local 5 | TTRSS_PATH_PLUGINS=${TTRSS_PATH}/plugins.local 6 | 7 | update_ttrss() 8 | { 9 | if [ -n "$TTRSS_GIT_TAG" ]; then 10 | echo "Updating Tiny Tiny RSS disabled (using tag '$TTRSS_GIT_TAG')" 11 | return 12 | fi 13 | 14 | echo "Updating: Tiny Tiny RSS" 15 | ( cd ${TTRSS_PATH} && git pull origin HEAD ) 16 | } 17 | 18 | update_plugin_mobilize() 19 | { 20 | echo "Updating: Mobilize plugin" 21 | ( cd ${TTRSS_PATH_PLUGINS}/mobilize && git pull origin HEAD ) 22 | 23 | # Patch ttrss-mobilize plugin for getting it to work. 24 | sed -i -e "s/