{
  "manifest_version": "1.0.0",
  "template": {
    "id": "992c4fcb-42d8-48a6-84de-ec09880f3078",
    "slug": "telegram-file-to-link-bot",
    "name": "Telegram File To Link Bot",
    "description": "Telegram bot that turns file uploads into secure public download links",
    "url": "https://railway.com/deploy/telegram-file-to-link-bot",
    "upstream": {
      "repo_url": "https://github.com/BigDaddyAman/telegram-file-to-link-bot"
    }
  },
  "status": "unvalidated",
  "validated_at": null,
  "success_rate_30d": null,
  "services": [
    {
      "name": "telegram-file-link-bot",
      "source": {
        "repo": "https://github.com/BigDaddyAman/telegram-file-to-link-bot"
      },
      "needs_volume": false,
      "http": true
    },
    {
      "name": "Postgres-l5gt",
      "source": {
        "image": "ghcr.io/railwayapp-templates/postgres-ssl:17"
      },
      "needs_volume": true,
      "volume_mount_path": "/var/lib/postgresql/data",
      "tcp_ports": [
        5432
      ],
      "http": false
    },
    {
      "name": "Redis",
      "source": {
        "image": "redis:8.2.1"
      },
      "needs_volume": true,
      "volume_mount_path": "/data",
      "tcp_ports": [
        6379
      ],
      "http": false
    }
  ],
  "required_inputs": [
    {
      "key": "API_ID",
      "service": "telegram-file-link-bot",
      "description": "Telegram API ID (get from https://my.telegram.org)",
      "secret": false,
      "strategy": "ask_user"
    },
    {
      "key": "API_HASH",
      "service": "telegram-file-link-bot",
      "description": "Telegram API hash (get from https://my.telegram.org)",
      "secret": false,
      "strategy": "ask_user"
    },
    {
      "key": "BASE_URL",
      "service": "telegram-file-link-bot",
      "description": "Public HTTPS URL where generated download links point to. Example: https://files.example.com Must match the domain serving this app.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_domain"
    },
    {
      "key": "BOT_TOKEN",
      "service": "telegram-file-link-bot",
      "description": "Telegram bot token (get from @BotFather)",
      "secret": true,
      "strategy": "generate",
      "generate": "random_base64_32"
    },
    {
      "key": "REDIS_URL",
      "service": "telegram-file-link-bot",
      "description": "Redis connection string",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "MAX_FILE_MB",
      "service": "telegram-file-link-bot",
      "description": "Maximum allowed file size (in MB) for uploads handled by the bot. Files larger than this limit will be rejected. Example values: 100, 500, 2048.",
      "secret": false,
      "strategy": "default",
      "default": "4096"
    },
    {
      "key": "DATABASE_URL",
      "service": "telegram-file-link-bot",
      "description": "DATABASE_URL – PostgreSQL connection string ",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "ADMIN_ENABLED",
      "service": "telegram-file-link-bot",
      "description": "Enable admin dashboard. Set to true or false. Default: false. Dashboard available at /admin when enabled.",
      "secret": false,
      "strategy": "default",
      "default": "false"
    },
    {
      "key": "STORAGE_BACKEND",
      "service": "telegram-file-link-bot",
      "description": "Storage backend for uploaded files. Use s3 on Railway (recommended) to ensure persistent storage via Storage Buckets. Using local on Railway is NOT persistent and data will be lost on redeploy. The s3 backend also supports any S3-compatible storage such as AWS S3, Cloudflare R2, MinIO, and similar services.",
      "secret": false,
      "strategy": "default",
      "default": "s3"
    },
    {
      "key": "AWS_ENDPOINT_URL",
      "service": "telegram-file-link-bot",
      "description": "S3-compatible storage endpoint URL (e.g. Railway Storage, Cloudflare R2, MinIO)",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "AWS_ACCESS_KEY_ID",
      "service": "telegram-file-link-bot",
      "description": "Access key ID for authenticating with the S3-compatible storage",
      "secret": true,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "AWS_DEFAULT_REGION",
      "service": "telegram-file-link-bot",
      "description": "Region for the S3-compatible storage service (auto on Railway)",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "AWS_S3_BUCKET_NAME",
      "service": "telegram-file-link-bot",
      "description": "Name of the S3 bucket used to store uploaded files",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "AWS_SECRET_ACCESS_KEY",
      "service": "telegram-file-link-bot",
      "description": "Secret access key for authenticating with the S3-compatible storage",
      "secret": true,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "GLOBAL_RATE_LIMIT_WINDOW",
      "service": "telegram-file-link-bot",
      "description": "Time window in seconds",
      "secret": false,
      "strategy": "default",
      "default": "10"
    },
    {
      "key": "GLOBAL_RATE_LIMIT_REQUESTS",
      "service": "telegram-file-link-bot",
      "description": "Max requests per IP",
      "secret": false,
      "strategy": "default",
      "default": "60"
    },
    {
      "key": "PGDATA",
      "service": "Postgres-l5gt",
      "description": "Location where the database will be initialized",
      "secret": false,
      "strategy": "default",
      "default": "/var/lib/postgresql/data/pgdata"
    },
    {
      "key": "PGHOST",
      "service": "Postgres-l5gt",
      "description": "Railway Private Domain Name.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "PGPORT",
      "service": "Postgres-l5gt",
      "description": "Port to connect to Postgres.",
      "secret": false,
      "strategy": "default",
      "default": "5432"
    },
    {
      "key": "PGUSER",
      "service": "Postgres-l5gt",
      "description": "Required variable for Data panel",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "PGDATABASE",
      "service": "Postgres-l5gt",
      "description": "Required variable for the data panel.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "PGPASSWORD",
      "service": "Postgres-l5gt",
      "description": "Required variable for Data panel",
      "secret": true,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "POSTGRES_DB",
      "service": "Postgres-l5gt",
      "description": "Default database created when image is started.",
      "secret": false,
      "strategy": "default",
      "default": "railway"
    },
    {
      "key": "DATABASE_URL",
      "service": "Postgres-l5gt",
      "description": "URL to connect to Postgres database.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "POSTGRES_USER",
      "service": "Postgres-l5gt",
      "description": "User to connect to Postgres DB",
      "secret": false,
      "strategy": "default",
      "default": "postgres"
    },
    {
      "key": "SSL_CERT_DAYS",
      "service": "Postgres-l5gt",
      "description": "SSL certificate expiry in days.",
      "secret": false,
      "strategy": "default",
      "default": "820"
    },
    {
      "key": "POSTGRES_PASSWORD",
      "service": "Postgres-l5gt",
      "description": "Password to connect to DB",
      "secret": true,
      "strategy": "generate",
      "generate": "strong_password"
    },
    {
      "key": "DATABASE_PUBLIC_URL",
      "service": "Postgres-l5gt",
      "description": "Public URL to connect to Postgres database, used by the Data panel.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "RAILWAY_DEPLOYMENT_DRAINING_SECONDS",
      "service": "Postgres-l5gt",
      "description": "Allow Postgres to cleanly shut down",
      "secret": false,
      "strategy": "default",
      "default": "60"
    },
    {
      "key": "REDISHOST",
      "service": "Redis",
      "description": "Provide a value for REDISHOST.",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "railway_private_domain"
    },
    {
      "key": "REDISPORT",
      "service": "Redis",
      "description": "Provide a value for REDISPORT.",
      "secret": false,
      "strategy": "default",
      "default": "6379"
    },
    {
      "key": "REDISUSER",
      "service": "Redis",
      "description": "Provide a value for REDISUSER.",
      "secret": false,
      "strategy": "default",
      "default": "default"
    },
    {
      "key": "REDIS_URL",
      "service": "Redis",
      "description": "Connection string for connecting to redis using the private network",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "REDISPASSWORD",
      "service": "Redis",
      "description": "Provide a value for REDISPASSWORD.",
      "secret": true,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    },
    {
      "key": "REDIS_PASSWORD",
      "service": "Redis",
      "description": "Provide a value for REDIS_PASSWORD.",
      "secret": true,
      "strategy": "generate",
      "generate": "strong_password"
    },
    {
      "key": "REDIS_PUBLIC_URL",
      "service": "Redis",
      "description": "Connection string for connecting to redis externally",
      "secret": false,
      "strategy": "railway_provided",
      "railway_source": "reference_variable"
    }
  ],
  "deploy": {
    "mcp": {
      "server": "railway",
      "tool": "deploy_template",
      "args": {
        "template_code": "telegram-file-to-link-bot"
      }
    },
    "cli": "railway deploy --template telegram-file-to-link-bot",
    "api": {
      "method": "POST",
      "path": "/graphql/v2",
      "body": {
        "query": "mutation templateDeploy($input: TemplateDeployV2Input!) { templateDeployV2(input: $input) { projectId workflowId } }",
        "variables": {
          "input": {
            "templateId": "992c4fcb-42d8-48a6-84de-ec09880f3078",
            "serializedConfig": {
              "buckets": {
                "68b6871d-4642-4ec5-9f54-25c92080b1c5": {
                  "name": "Bucket"
                }
              },
              "services": {
                "8a81f26b-b8f7-43b7-9093-049f04d8ad36": {
                  "icon": "https://devicons.railway.app/telegra",
                  "name": "telegram-file-link-bot",
                  "deploy": {
                    "startCommand": null,
                    "healthcheckPath": null,
                    "restartPolicyType": "ON_FAILURE",
                    "restartPolicyMaxRetries": 10
                  },
                  "source": {
                    "repo": "https://github.com/BigDaddyAman/telegram-file-to-link-bot",
                    "rootDirectory": null
                  },
                  "variables": {
                    "API_ID": {
                      "isOptional": false,
                      "description": "Telegram API ID (get from https://my.telegram.org)",
                      "defaultValue": "{{API_ID}}"
                    },
                    "API_HASH": {
                      "isOptional": false,
                      "description": "Telegram API hash (get from https://my.telegram.org)",
                      "defaultValue": "{{API_HASH}}"
                    },
                    "BASE_URL": {
                      "isOptional": true,
                      "description": "Public HTTPS URL where generated download links point to. Example: https://files.example.com Must match the domain serving this app.",
                      "defaultValue": "https://${{RAILWAY_PUBLIC_DOMAIN}}"
                    },
                    "BOT_TOKEN": {
                      "isOptional": false,
                      "description": "Telegram bot token (get from @BotFather)",
                      "defaultValue": "{{BOT_TOKEN}}"
                    },
                    "REDIS_URL": {
                      "isOptional": false,
                      "description": "Redis connection string",
                      "defaultValue": "${{Redis.REDIS_URL}}"
                    },
                    "ADMIN_EMAIL": {
                      "isOptional": true,
                      "description": "Admin login email address. Example: admin@example.com Required only if ADMIN_ENABLED=true.",
                      "defaultValue": ""
                    },
                    "MAX_FILE_MB": {
                      "isOptional": true,
                      "description": "Maximum allowed file size (in MB) for uploads handled by the bot. Files larger than this limit will be rejected. Example values: 100, 500, 2048.",
                      "defaultValue": "4096"
                    },
                    "DATABASE_URL": {
                      "isOptional": false,
                      "description": "DATABASE_URL – PostgreSQL connection string ",
                      "defaultValue": "${{Postgres-l5gt.DATABASE_URL}}"
                    },
                    "ADMIN_ENABLED": {
                      "isOptional": true,
                      "description": "Enable admin dashboard. Set to true or false. Default: false. Dashboard available at /admin when enabled.",
                      "defaultValue": "false"
                    },
                    "ADMIN_PASSWORD": {
                      "isOptional": true,
                      "description": "Admin login password. Example: change_me Required only if ADMIN_ENABLED=true.",
                      "defaultValue": ""
                    },
                    "SESSION_SECRET": {
                      "isOptional": true,
                      "description": "Random secret key used to sign admin sessions. Example: replace_with_random_secret Required only if ADMIN_ENABLED=true.",
                      "defaultValue": ""
                    },
                    "STORAGE_BACKEND": {
                      "isOptional": true,
                      "description": "Storage backend for uploaded files. Use s3 on Railway (recommended) to ensure persistent storage via Storage Buckets. Using local on Railway is NOT persistent and data will be lost on redeploy. The s3 backend also supports any S3-compatible storage such as AWS S3, Cloudflare R2, MinIO, and similar services.",
                      "defaultValue": "s3"
                    },
                    "ALLOWED_USER_IDS": {
                      "isOptional": true,
                      "description": "Comma-separated Telegram numeric user IDs allowed to upload files. Example: 123456789,987654321 Leave empty to allow everyone.  To get your user ID: open Telegram → search @userinfobot → press Start.",
                      "defaultValue": ""
                    },
                    "AWS_ENDPOINT_URL": {
                      "isOptional": true,
                      "description": "S3-compatible storage endpoint URL (e.g. Railway Storage, Cloudflare R2, MinIO)",
                      "defaultValue": "${{Bucket.ENDPOINT}}"
                    },
                    "AWS_ACCESS_KEY_ID": {
                      "isOptional": true,
                      "description": "Access key ID for authenticating with the S3-compatible storage",
                      "defaultValue": "${{Bucket.ACCESS_KEY_ID}}"
                    },
                    "AWS_DEFAULT_REGION": {
                      "isOptional": true,
                      "description": "Region for the S3-compatible storage service (auto on Railway)",
                      "defaultValue": "${{Bucket.REGION}}"
                    },
                    "AWS_S3_BUCKET_NAME": {
                      "isOptional": true,
                      "description": "Name of the S3 bucket used to store uploaded files",
                      "defaultValue": "${{Bucket.BUCKET}}"
                    },
                    "AWS_SECRET_ACCESS_KEY": {
                      "isOptional": true,
                      "description": "Secret access key for authenticating with the S3-compatible storage",
                      "defaultValue": "${{Bucket.SECRET_ACCESS_KEY}}"
                    },
                    "GLOBAL_RATE_LIMIT_WINDOW": {
                      "isOptional": true,
                      "description": "Time window in seconds",
                      "defaultValue": "10"
                    },
                    "MAX_CONCURRENT_TRANSFERS": {
                      "isOptional": true,
                      "description": "Maximum number of concurrent file uploads/downloads. Default: 3.",
                      "defaultValue": ""
                    },
                    "GLOBAL_RATE_LIMIT_REQUESTS": {
                      "isOptional": true,
                      "description": "Max requests per IP",
                      "defaultValue": "60"
                    }
                  },
                  "networking": {
                    "serviceDomains": {
                      "<hasDomain>:8080": {
                        "port": 8080
                      }
                    }
                  }
                },
                "9209816b-c2e8-407f-bfff-914e4c102498": {
                  "icon": "https://devicons.railway.app/i/postgresql.svg",
                  "name": "Postgres-l5gt",
                  "build": {},
                  "deploy": {
                    "requiredMountPath": "/var/lib/postgresql/data"
                  },
                  "source": {
                    "image": "ghcr.io/railwayapp-templates/postgres-ssl:17"
                  },
                  "variables": {
                    "PGDATA": {
                      "isOptional": false,
                      "description": "Location where the database will be initialized",
                      "defaultValue": "/var/lib/postgresql/data/pgdata"
                    },
                    "PGHOST": {
                      "isOptional": false,
                      "description": "Railway Private Domain Name.",
                      "defaultValue": "${{RAILWAY_PRIVATE_DOMAIN}}"
                    },
                    "PGPORT": {
                      "isOptional": false,
                      "description": "Port to connect to Postgres.",
                      "defaultValue": "5432"
                    },
                    "PGUSER": {
                      "isOptional": false,
                      "description": "Required variable for Data panel",
                      "defaultValue": "${{ POSTGRES_USER }}"
                    },
                    "PGDATABASE": {
                      "isOptional": false,
                      "description": "Required variable for the data panel.",
                      "defaultValue": "${{POSTGRES_DB}}"
                    },
                    "PGPASSWORD": {
                      "isOptional": false,
                      "description": "Required variable for Data panel",
                      "defaultValue": "${{POSTGRES_PASSWORD}}"
                    },
                    "POSTGRES_DB": {
                      "isOptional": false,
                      "description": "Default database created when image is started.",
                      "defaultValue": "railway"
                    },
                    "DATABASE_URL": {
                      "isOptional": false,
                      "description": "URL to connect to Postgres database.",
                      "defaultValue": "postgresql://${{PGUSER}}:${{POSTGRES_PASSWORD}}@${{RAILWAY_PRIVATE_DOMAIN}}:5432/${{PGDATABASE}}"
                    },
                    "POSTGRES_USER": {
                      "isOptional": false,
                      "description": "User to connect to Postgres DB",
                      "defaultValue": "postgres"
                    },
                    "SSL_CERT_DAYS": {
                      "isOptional": true,
                      "description": "SSL certificate expiry in days.",
                      "defaultValue": "820"
                    },
                    "POSTGRES_PASSWORD": {
                      "isOptional": false,
                      "description": "Password to connect to DB",
                      "defaultValue": "{{POSTGRES_PASSWORD}}"
                    },
                    "DATABASE_PUBLIC_URL": {
                      "description": "Public URL to connect to Postgres database, used by the Data panel.",
                      "defaultValue": "postgresql://${{PGUSER}}:${{POSTGRES_PASSWORD}}@${{RAILWAY_TCP_PROXY_DOMAIN}}:${{RAILWAY_TCP_PROXY_PORT}}/${{PGDATABASE}}"
                    },
                    "RAILWAY_DEPLOYMENT_DRAINING_SECONDS": {
                      "isOptional": false,
                      "description": "Allow Postgres to cleanly shut down",
                      "defaultValue": "60"
                    }
                  },
                  "networking": {
                    "tcpProxies": {
                      "5432": {}
                    },
                    "serviceDomains": {}
                  },
                  "volumeMounts": {
                    "9209816b-c2e8-407f-bfff-914e4c102498": {
                      "mountPath": "/var/lib/postgresql/data"
                    }
                  },
                  "haTemplateCode": "postgres-ha",
                  "haConversionConfig": {
                    "edge": {
                      "label": "Reverse Proxy",
                      "options": [
                        2,
                        3,
                        4,
                        5
                      ],
                      "nodeLabel": "HAProxy",
                      "description": "Routes traffic to the primary",
                      "defaultValue": 3
                    },
                    "replica": {
                      "label": "Replicas",
                      "options": [
                        2,
                        3,
                        4,
                        5,
                        6,
                        7
                      ],
                      "nodeLabel": "Postgres",
                      "description": "Streaming replicas for automatic failover",
                      "defaultValue": 2
                    },
                    "internal": {
                      "label": "Coordinator Nodes",
                      "options": [
                        3,
                        5,
                        7,
                        9
                      ],
                      "nodeLabel": "etcd",
                      "defaultValue": 3
                    },
                    "description": "This will convert your Postgres database to an HA cluster with Patroni failover and etcd consensus."
                  }
                },
                "d15eb965-6017-4062-a053-8d1d8fc06b03": {
                  "icon": "https://cdn.sanity.io/images/sy1jschh/production/0ce0bfdcfbdbf69662b1116671f97c2dd788b655-157x157.svg",
                  "name": "Redis",
                  "deploy": {
                    "startCommand": "/bin/sh -c \"rm -rf $RAILWAY_VOLUME_MOUNT_PATH/lost+found/ && exec docker-entrypoint.sh redis-server --requirepass $REDIS_PASSWORD --save 60 1 --dir $RAILWAY_VOLUME_MOUNT_PATH\""
                  },
                  "source": {
                    "image": "redis:8.2.1"
                  },
                  "variables": {
                    "REDISHOST": {
                      "isOptional": false,
                      "defaultValue": "${{RAILWAY_PRIVATE_DOMAIN}}"
                    },
                    "REDISPORT": {
                      "isOptional": false,
                      "defaultValue": "6379"
                    },
                    "REDISUSER": {
                      "isOptional": false,
                      "defaultValue": "default"
                    },
                    "REDIS_URL": {
                      "isOptional": false,
                      "description": "Connection string for connecting to redis using the private network",
                      "defaultValue": "redis://${{ REDISUSER }}:${{ REDIS_PASSWORD }}@${{ REDISHOST }}:${{ REDISPORT }}"
                    },
                    "REDISPASSWORD": {
                      "isOptional": false,
                      "defaultValue": "${{REDIS_PASSWORD}}"
                    },
                    "REDIS_PASSWORD": {
                      "isOptional": false,
                      "defaultValue": "{{REDIS_PASSWORD}}"
                    },
                    "REDIS_PUBLIC_URL": {
                      "description": "Connection string for connecting to redis externally",
                      "defaultValue": "redis://default:${{ REDIS_PASSWORD }}@${{ RAILWAY_TCP_PROXY_DOMAIN }}:${{ RAILWAY_TCP_PROXY_PORT }}"
                    }
                  },
                  "networking": {
                    "tcpProxies": {
                      "6379": {}
                    }
                  },
                  "volumeMounts": {
                    "d15eb965-6017-4062-a053-8d1d8fc06b03": {
                      "mountPath": "/data"
                    }
                  },
                  "haTemplateCode": "redis-ha",
                  "haConversionConfig": {
                    "edge": {
                      "label": "Reverse Proxies",
                      "options": [
                        1,
                        2
                      ],
                      "nodeLabel": "HAProxy",
                      "description": "HAProxy instances routing writes to the primary",
                      "defaultValue": 2
                    },
                    "replica": {
                      "label": "Redis Replicas",
                      "options": [
                        2,
                        3,
                        4,
                        5
                      ],
                      "nodeLabel": "Redis",
                      "description": "Nodes running Redis + colocated Sentinel",
                      "defaultValue": 2
                    },
                    "internal": null,
                    "description": "This will convert your Redis instance to an HA cluster with Sentinel failover and HAProxy routing to the primary."
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "post_deploy": {},
  "resources": {
    "expected_services": 3,
    "needs_volume": true
  },
  "generated_at": "2026-09-13T10:14:32.962Z",
  "generator_version": "0.1.0"
}
